text:
<?php
/**
 * Abstract Tree class stolen from http://articles.sitepoint.com/article/hierarchical-data-database/3
 * @author gizmore
 */
abstract class GWF_Tree extends GDO
{
	public abstract function getColumnPrefix();# { return 'tree_'; }

	###########
	### GDO ###
	###########
	public function getColumnDefines()
	{
		$pre = $this->getColumnPrefix();
		return array(
			$pre.'tree_id' => array(GDO::AUTO_INCREMENT),
			$pre.'tree_key' => array(GDO::VARCHAR|GDO::ASCII|GDO::CASE_I, GDO::NOT_NULL, 255),
			$pre.'tree_pid' => array(GDO::UINT|GDO::INDEX),
			$pre.'tree_left' => array(GDO::UINT|GDO::INDEX),
			$pre.'tree_right' => array(GDO::UINT|GDO::INDEX),
		);
	}
	public function getIDColumn() { return $this->getColumnPrefix().'tree_id'; }
	public function getID() { return $this->getVar($this->getIDColumn()); }
	public function getKeyColumn() { return $this->getColumnPrefix().'tree_key'; }
	public function getKey() { return $this->getVar($this->getKeyColumn()); }
	public function getLeftColumn() { return $this->getColumnPrefix().'tree_left'; }
	public function getLeft() { return $this->getVar($this->getLeftColumn()); }
	public function getRightColumn() { return $this->getColumnPrefix().'tree_right'; }
	public function getRight() { return $this->getVar($this->getRightColumn()); }
	public function getParentColumn() { return $this->getColumnPrefix().'tree_pid'; }
	public function getParentID() { return $this->getVar($this->getParentColumn()); }

	public function getTree()
	{
		$pre = $this->getColumnPrefix();
		$left = $pre.'tree_left';
		$l = $this->getLeft();
		$r = $this->getRight();
		return $this->selectAll("{$pre}tree_id, {$pre}tree_name", "$left BETWEEN $l AND $r", "$left ASC");

	}

//	public function insertTreeItem($name, $pid=0)
//	{
//
//	}

	public function rebuildFullTree()
	{
		return $this->rebuildTree(0, 0);
	}

	private function rebuildTree($parent, $left)
	{
// 		$parent = (int)$parent;
// 		$left = (int)$left;
		$right = $left + 1;

		$p = $this->getParentColumn();
		$idc = $this->getIDColumn();

		$result = $this->selectColumn($idc, "$p=$parent");
		foreach ($result as $id)
		{
			$right = $this->rebuildTree($id, $right);
		}

		$l = $this->getLeftColumn();
		$r = $this->getRightColumn();
		$this->update("$l=$left, $r=$right", "$idc=$parent");

		return $right+1;
	}
}


text:
package me.devsaki.hentoid.database;

import static com.annimon.stream.Collectors.toList;

import android.content.Context;
import android.util.SparseIntArray;

import androidx.annotation.NonNull;

import com.annimon.stream.Collectors;
import com.annimon.stream.Stream;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.threeten.bp.Instant;

import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;

import javax.annotation.Nullable;

import io.objectbox.Box;
import io.objectbox.BoxStore;
import io.objectbox.Property;
import io.objectbox.android.AndroidObjectBrowser;
import io.objectbox.query.Query;
import io.objectbox.query.QueryBuilder;
import io.objectbox.relation.ToMany;
import me.devsaki.hentoid.BuildConfig;
import me.devsaki.hentoid.core.Consts;
import me.devsaki.hentoid.database.domains.Attribute;
import me.devsaki.hentoid.database.domains.AttributeLocation;
import me.devsaki.hentoid.database.domains.AttributeMap;
import me.devsaki.hentoid.database.domains.Attribute_;
import me.devsaki.hentoid.database.domains.Chapter;
import me.devsaki.hentoid.database.domains.Chapter_;
import me.devsaki.hentoid.database.domains.Content;
import me.devsaki.hentoid.database.domains.Content_;
import me.devsaki.hentoid.database.domains.ErrorRecord;
import me.devsaki.hentoid.database.domains.ErrorRecord_;
import me.devsaki.hentoid.database.domains.Group;
import me.devsaki.hentoid.database.domains.GroupItem;
import me.devsaki.hentoid.database.domains.GroupItem_;
import me.devsaki.hentoid.database.domains.Group_;
import me.devsaki.hentoid.database.domains.ImageFile;
import me.devsaki.hentoid.database.domains.ImageFile_;
import me.devsaki.hentoid.database.domains.MyObjectBox;
import me.devsaki.hentoid.database.domains.QueueRecord;
import me.devsaki.hentoid.database.domains.QueueRecord_;
import me.devsaki.hentoid.database.domains.ShuffleRecord;
import me.devsaki.hentoid.database.domains.SiteBookmark;
import me.devsaki.hentoid.database.domains.SiteBookmark_;
import me.devsaki.hentoid.database.domains.SiteHistory;
import me.devsaki.hentoid.database.domains.SiteHistory_;
import me.devsaki.hentoid.enums.AttributeType;
import me.devsaki.hentoid.enums.Grouping;
import me.devsaki.hentoid.enums.Site;
import me.devsaki.hentoid.enums.StatusContent;
import me.devsaki.hentoid.util.ContentHelper;
import me.devsaki.hentoid.util.Helper;
import me.devsaki.hentoid.util.Preferences;
import me.devsaki.hentoid.util.RandomSeedSingleton;
import timber.log.Timber;

public class ObjectBoxDB {

    // Status displayed in the library view (all books of the library; both internal and external)
    private static final int[] libraryStatus = ContentHelper.getLibraryStatuses();
    private static final int[] queueStatus = ContentHelper.getQueueStatuses();
    private static final int[] libraryQueueStatus = ArrayUtils.addAll(libraryStatus, queueStatus);

    private static final long DAY_IN_MILLIS = 1000L * 60 * 60 * 24;

    private static ObjectBoxDB instance;

    private final BoxStore store;

    private ObjectBoxDB(Context context) {
        store = MyObjectBox.builder().androidContext(context.getApplicationContext()).maxSizeInKByte(Preferences.getMaxDbSizeKb()).build();

        if (BuildConfig.DEBUG && BuildConfig.INCLUDE_OBJECTBOX_BROWSER) {
            boolean started = new AndroidObjectBrowser(store).start(context.getApplicationContext());
            Timber.i("ObjectBrowser started: %s", started);
        }
    }

    // For testing (store generated by the test framework)
    private ObjectBoxDB(BoxStore store) {
        this.store = store;
    }

    // Use this to get db instance
    public static synchronized ObjectBoxDB getInstance(Context context) {
        // Use application context only
        if (instance == null) {
            instance = new ObjectBoxDB(context);
        }

        return instance;
    }

    // Use this to get db instance for testing (store generated by the test framework)
    public static synchronized ObjectBoxDB getInstance(BoxStore store) {
        // Use application context only
        if (instance == null) {
            instance = new ObjectBoxDB(store);
        }

        return instance;
    }

    void closeThreadResources() {
        store.closeThreadResources();
    }

    long getDbSizeBytes() {
        return store.sizeOnDisk();
    }

    long insertContent(Content content) {
        ToMany<Attribute> attributes = content.getAttributes();
        Box<Attribute> attrBox = store.boxFor(Attribute.class);
        Query<Attribute> attrByUniqueKey = attrBox.query().equal(Attribute_.type, 0).equal(Attribute_.name, "", QueryBuilder.StringOrder.CASE_INSENSITIVE).build();

        return store.callInTxNoException(() -> {
            // Master data management managed manually
            // Ensure all known attributes are replaced by their ID before being inserted
            // Watch https://github.com/objectbox/objectbox-java/issues/509 for a lighter solution based on @Unique annotation
            Attribute dbAttr;
            Attribute inputAttr;
            if (attributes != null) {
                // This transaction may consume a lot of DB readers depending on the number of attributes involved
                for (int i = 0; i < attributes.size(); i++) {
                    inputAttr = attributes.get(i);
                    dbAttr = attrByUniqueKey.setParameter(Attribute_.name, inputAttr.getName())
                            .setParameter(Attribute_.type, inputAttr.getType().getCode())
                            .findFirst();
                    if (dbAttr != null) {
                        attributes.set(i, dbAttr); // If existing -> set the existing attribute
                        dbAttr.addLocationsFrom(inputAttr);
                        attrBox.put(dbAttr);
                    } else {
                        inputAttr.setName(inputAttr.getName().toLowerCase().trim()); // If new -> normalize the attribute name
                    }
                }
            }

            return store.boxFor(Content.class).put(content);
        });
    }

    // Faster alternative to insertContent when Content fields only need to be updated
    void updateContentObject(Content content) {
        store.boxFor(Content.class).put(content);
    }

    public void updateContentStatus(@NonNull final StatusContent updateFrom, @NonNull final StatusContent updateTo) {
        List<Content> contentList = selectContentByStatus(updateFrom);
        for (Content c : contentList) c.setStatus(updateTo);
        store.boxFor(Content.class).put(contentList);
    }

    List<Content> selectContentByStatus(StatusContent status) {
        return selectContentByStatusCodes(new int[]{status.getCode()});
    }

    private List<Content> selectContentByStatusCodes(int[] statusCodes) {
        return store.boxFor(Content.class).query().in(Content_.status, statusCodes).build().find();
    }

    Query<Content> selectAllInternalBooksQ(boolean favsOnly) {
        // All statuses except SAVED, DOWNLOADING, PAUSED and ERROR that imply the book is in the download queue
        // and EXTERNAL because we only want to manage internal books here
        int[] storedContentStatus = new int[]{
                StatusContent.DOWNLOADED.getCode(),
                StatusContent.MIGRATED.getCode(),
                StatusContent.IGNORED.getCode(),
                StatusContent.UNHANDLED_ERROR.getCode(),
                StatusContent.CANCELED.getCode()
        };
        QueryBuilder<Content> query = store.boxFor(Content.class).query().in(Content_.status, storedContentStatus);
        if (favsOnly) query.equal(Content_.favourite, true);
        return query.build();
    }

    Query<Content> selectAllExternalBooksQ() {
        return store.boxFor(Content.class).query().equal(Content_.status, StatusContent.EXTERNAL.getCode()).build();
    }

    Query<Content> selectAllErrorJsonBooksQ() {
        return store.boxFor(Content.class).query().equal(Content_.status, StatusContent.ERROR.getCode()).notNull(Content_.jsonUri).notEqual(Content_.jsonUri, "", QueryBuilder.StringOrder.CASE_INSENSITIVE).build();
    }

    Query<Content> selectAllQueueBooksQ() {
        return store.boxFor(Content.class).query().in(Content_.status, ContentHelper.getQueueStatuses()).build();
    }

    Query<Content> selectAllFlaggedBooksQ() {
        return store.boxFor(Content.class).query().equal(Content_.isFlaggedForDeletion, true).build();
    }

    Query<Content> selectAllMarkedBooksQ() {
        return store.boxFor(Content.class).query().equal(Content_.isBeingDeleted, true).build();
    }

    void flagContents(List<Content> contentList, boolean flag) {
        for (Content c : contentList) c.setFlaggedForDeletion(flag);
        store.boxFor(Content.class).put(contentList);
    }

    void markContents(List<Content> contentList, boolean flag) {
        for (Content c : contentList) c.setIsBeingDeleted(flag);
        store.boxFor(Content.class).put(contentList);
    }

    void deleteContentById(long contentId) {
        deleteContentById(new long[]{contentId});
    }

    /**
     * Remove the given content and all related objects from the DB
     * NB : ObjectBox v2.3.1 does not support cascade delete, so everything has to be done manually
     *
     * @param contentId IDs of the contents to be removed from the DB
     */
    void deleteContentById(long[] contentId) {
        Box<ErrorRecord> errorBox = store.boxFor(ErrorRecord.class);
        Box<ImageFile> imageFileBox = store.boxFor(ImageFile.class);
        Box<Chapter> chapterBox = store.boxFor(Chapter.class);
        Box<Content> contentBox = store.boxFor(Content.class);
        Box<GroupItem> groupItemBox = store.boxFor(GroupItem.class);
        Box<Group> groupBox = store.boxFor(Group.class);

        for (long id : contentId) {
            Content c = contentBox.get(id);
            if (c != null) {
                store.runInTx(() -> {
                    if (c.getImageFiles() != null) {
                        imageFileBox.remove(c.getImageFiles());
                        c.getImageFiles().clear();                                      // Clear links to all imageFiles
                    }

                    if (c.getChapters() != null) {
                        chapterBox.remove(c.getChapters());
                        c.getChapters().clear();                                      // Clear links to all chapters
                    }

                    if (c.getErrorLog() != null) {
                        errorBox.remove(c.getErrorLog());
                        c.getErrorLog().clear();                                    // Clear links to all errorRecords
                    }

                    // Clear links to all attributes
                    // NB : Properly removing all attributes here is too costly, especially on large collections
                    // It's done by calling cleanupOrphanAttributes
                    c.getAttributes().clear();

                    // Delete corresponding groupItem
                    List<GroupItem> groupItems = groupItemBox.query().equal(GroupItem_.contentId, id).build().find();
                    for (GroupItem groupItem : groupItems) {
                        // If we're not in the Custom grouping and it's the only item of its group, delete the group
                        Group g = groupItem.group.getTarget();
                        if (g != null && !g.grouping.equals(Grouping.CUSTOM) && g.items.size() < 2)
                            groupBox.remove(g);
                        // Delete the item
                        groupItemBox.remove(groupItem);
                    }

                    contentBox.remove(c);                                           // Remove the content itself
                });
            }
        }
    }

    /**
     * Cleanup all Attributes that don't have any backlink among content
     */
    public void cleanupOrphanAttributes() {
        Box<Attribute> attributeBox = store.boxFor(Attribute.class);
        Box<AttributeLocation> locationBox = store.boxFor(AttributeLocation.class);

        // Stream the collection to get the attributes to clean
        List<Attribute> attrsToClean = new ArrayList<>();
        Query<Attribute> attrQuery = attributeBox.query().build();
        attrQuery.forEach(
                attr -> {
                    if (attr.contents.isEmpty()) {
                        Timber.i(">> Found empty attr : %s", attr.getName());
                        attrsToClean.add(attr);
                    }
                }
        );

        // Clean the attributes
        for (Attribute attr : attrsToClean) {
            locationBox.remove(attr.getLocations());
            attr.getLocations().clear();                                           // Clear location links
            attributeBox.remove(attr);                                             // Delete the attribute itself
        }
    }

    List<Content> selectQueueContents() {
        List<Content> result = new ArrayList<>();
        List<QueueRecord> queueRecords = selectQueueRecordsQ(null).find();
        for (QueueRecord q : queueRecords) result.add(q.getContent().getTarget());
        return result;
    }

    Query<QueueRecord> selectQueueRecordsQ(String query) {
        QueryBuilder<QueueRecord> qb = store.boxFor(QueueRecord.class).query();
        // Universal search inside contents
        if (query != null && !query.isEmpty()) {
            long[] contentIds = selectContentUniversalId(
                    query,
                    -1,
                    false,
                    false,
                    Preferences.Constant.ORDER_FIELD_NONE,
                    false,
                    false,
                    false,
                    ContentHelper.getQueueTabStatuses()
            );
            qb.in(QueueRecord_.contentId, contentIds);
        }
        return qb.order(QueueRecord_.rank).build();
    }

    boolean isContentInQueue(@NonNull final Content c) {
        return store.boxFor(QueueRecord.class).query().equal(QueueRecord_.contentId, c.getId()).build().count() > 0;
    }

    long selectMaxQueueOrder() {
        return store.boxFor(QueueRecord.class).query().build().property(QueueRecord_.rank).max();
    }

    void insertQueueRecord(@NonNull final QueueRecord qr) {
        store.boxFor(QueueRecord.class).put(qr);
    }

    void insertQueue(long contentId, int order) {
        store.boxFor(QueueRecord.class).put(new QueueRecord(contentId, order));
    }

    void updateQueue(@NonNull final List<QueueRecord> queue) {
        Box<QueueRecord> queueRecordBox = store.boxFor(QueueRecord.class);
        queueRecordBox.put(queue);
    }

    void deleteQueue(@NonNull Content content) {
        deleteQueue(content.getId());
    }

    void deleteQueue(int queueIndex) {
        store.boxFor(QueueRecord.class).remove(selectQueueRecordsQ(null).find().get(queueIndex).id);
    }

    void deleteQueue() {
        store.boxFor(QueueRecord.class).removeAll();
    }

    private void deleteQueue(long contentId) {
        Box<QueueRecord> queueRecordBox = store.boxFor(QueueRecord.class);
        QueueRecord record = queueRecordBox.query().equal(QueueRecord_.contentId, contentId).build().findFirst();

        if (record != null) queueRecordBox.remove(record);
    }

    Query<Content> selectVisibleContentQ() {
        return selectContentSearchContentQ("", -1, Collections.emptyList(), false, false, Preferences.Constant.ORDER_FIELD_NONE, false, false, false);
    }

    @Nullable
    Content selectContentById(long id) {
        return store.boxFor(Content.class).get(id);
    }

    @Nullable
    List<Content> selectContentById(List<Long> id) {
        return store.boxFor(Content.class).get(id);
    }

    @Nullable
    Content selectContentBySourceAndUrl(@NonNull Site site, @NonNull String contentUrl, @NonNull String coverUrlStart) {
        // TODO combine these two queries with an OR
        Content result = store.boxFor(Content.class).query().notEqual(Content_.url, "", QueryBuilder.StringOrder.CASE_INSENSITIVE).equal(Content_.url, contentUrl, QueryBuilder.StringOrder.CASE_INSENSITIVE).equal(Content_.site, site.getCode()).build().findFirst();
        if (null == result && !coverUrlStart.isEmpty())
            result = store.boxFor(Content.class).query().notEqual(Content_.coverImageUrl, "", QueryBuilder.StringOrder.CASE_INSENSITIVE).startsWith(Content_.coverImageUrl, coverUrlStart, QueryBuilder.StringOrder.CASE_INSENSITIVE).equal(Content_.site, site.getCode()).build().findFirst();
        return result;
    }

    @Nullable
    Content selectContentEndWithStorageUri(@NonNull final String folderUriEnd, boolean onlyFlagged) {
        QueryBuilder<Content> queryBuilder = store.boxFor(Content.class).query().endsWith(Content_.storageUri, folderUriEnd, QueryBuilder.StringOrder.CASE_INSENSITIVE);
        if (onlyFlagged) queryBuilder.equal(Content_.isFlaggedForDeletion, true);

        return queryBuilder.build().findFirst();
    }

    private long[] getIdsFromAttributes(@NonNull List<Attribute> attrs) {
        if (attrs.isEmpty()) return new long[0];

        if (attrs.get(0).isExcluded()) {
            long[] filteredBooks = selectFilteredContent(attrs, false, false, false);

            // Find all content positively matching the given attributes
            // TODO... but the attrs are already negative ^^"
            QueryBuilder<Content> query = store.boxFor(Content.class).query();
            query.in(Content_.status, libraryStatus);
            query.in(Content_.id, filteredBooks);
            List<Content> content = query.build().find();

            // Extract sites from them
            Map<Site, List<Content>> contentPerSite = Stream.of(content).collect(Collectors.groupingBy(Content::getSite));
            Set<Long> filteredSiteCodes = new HashSet<>();
            for (Map.Entry<Site, List<Content>> entry : contentPerSite.entrySet()) {
                Site site = entry.getKey();
                filteredSiteCodes.add((long) site.getCode());
            }
            List<Long> result = new ArrayList<>(filteredSiteCodes);

            return Helper.getPrimitiveLongArrayFromList(result);
        } else {
            long[] result = new long[attrs.size()];
            if (!attrs.isEmpty()) {
                int index = 0;
                for (Attribute a : attrs) result[index++] = a.getId();
            }
            return result;
        }
    }

    private void applySortOrder(
            QueryBuilder<Content> query,
            int orderField,
            boolean orderDesc) {
        // Random ordering is tricky (see https://github.com/objectbox/objectbox-java/issues/17)
        // => Implemented post-query build
        if (orderField == Preferences.Constant.ORDER_FIELD_RANDOM) return;
        // Custom ordering depends on another "table"
        // => Implemented post-query build
        if (orderField == Preferences.Constant.ORDER_FIELD_CUSTOM) {
            //query.sort(new Content.GroupItemOrderComparator(groupId)); // doesn't work with PagedList because it uses Query.find(final long offset, final long limit)
            //query.backlink(GroupItem_.content).order(GroupItem_.order); // doesn't work yet (see https://github.com/objectbox/objectbox-java/issues/141)
            return;
        }

        Property<Content> field = getPropertyFromField(orderField);
        if (null == field) return;

        if (orderDesc) query.orderDesc(field);
        else query.order(field);

        // Specifics sub-sorting fields when ordering by reads
        if (orderField == Preferences.Constant.ORDER_FIELD_READS) {
            if (orderDesc) query.orderDesc(Content_.lastReadDate);
            else query.order(Content_.lastReadDate).orderDesc(Content_.downloadDate);
        }
    }

    @Nullable
    private Property<Content> getPropertyFromField(int prefsFieldCode) {
        switch (prefsFieldCode) {
            case Preferences.Constant.ORDER_FIELD_TITLE:
                return Content_.title;
            case Preferences.Constant.ORDER_FIELD_ARTIST:
                return Content_.author; // Might not be what users want when there are multiple authors
            case Preferences.Constant.ORDER_FIELD_NB_PAGES:
                return Content_.qtyPages;
            case Preferences.Constant.ORDER_FIELD_DOWNLOAD_DATE:
                return Content_.downloadDate;
            case Preferences.Constant.ORDER_FIELD_UPLOAD_DATE:
                return Content_.uploadDate;
            case Preferences.Constant.ORDER_FIELD_READ_DATE:
                return Content_.lastReadDate;
            case Preferences.Constant.ORDER_FIELD_READS:
                return Content_.reads;
            case Preferences.Constant.ORDER_FIELD_SIZE:
                return Content_.size;
            case Preferences.Constant.ORDER_FIELD_READ_PROGRESS:
                return Content_.readProgress;
            default:
                return null;
        }
    }

    Query<Content> selectNoContentQ() {
        return store.boxFor(Content.class).query().equal(Content_.id, -1).build();
    }

    Query<Content> selectContentSearchContentQ(
            String title,
            long groupId,
            List<Attribute> metadata,
            boolean filterBookFavourites,
            boolean filterPageFavourites,
            int orderField,
            boolean orderDesc,
            boolean bookCompletedOnly,
            boolean bookNotCompletedOnly) {
        if (Preferences.Constant.ORDER_FIELD_CUSTOM == orderField)
            return store.boxFor(Content.class).query().build();

        AttributeMap metadataMap = new AttributeMap();
        metadataMap.addAll(metadata);

        boolean hasTitleFilter = (title != null && title.length() > 0);
        boolean hasGroupFilter = (groupId > 0);
        List<Attribute> sources = metadataMap.get(AttributeType.SOURCE);
        boolean hasSiteFilter = metadataMap.containsKey(AttributeType.SOURCE)
                && (sources != null)
                && !(sources.isEmpty());
        boolean hasTagFilter = metadataMap.keySet().size() > (hasSiteFilter ? 1 : 0);

        QueryBuilder<Content> query = store.boxFor(Content.class).query();
        query.in(Content_.status, libraryStatus);

        if (hasSiteFilter)
            query.in(Content_.site, getIdsFromAttributes(sources));
        if (filterBookFavourites) query.equal(Content_.favourite, true);

        if (bookCompletedOnly) query.equal(Content_.completed, true);
        else if (bookNotCompletedOnly) query.equal(Content_.completed, false);

        if (hasTitleFilter)
            query.contains(Content_.title, title, QueryBuilder.StringOrder.CASE_INSENSITIVE);
        if (hasTagFilter) {
            for (Map.Entry<AttributeType, List<Attribute>> entry : metadataMap.entrySet()) {
                AttributeType attrType = entry.getKey();
                if (!attrType.equals(AttributeType.SOURCE)) { // Not a "real" attribute in database
                    List<Attribute> attrs = entry.getValue();
                    if (attrs != null && !attrs.isEmpty()) {
                        query.in(Content_.id, selectFilteredContent(attrs, false, false, false));
                    }
                }
            }
        }
        if (filterPageFavourites) filterWithPageFavs(query);
        if (hasGroupFilter) {
            Group group = store.boxFor(Group.class).get(groupId);
            if (group != null && group.grouping.equals(Grouping.DL_DATE)) // According to days since download date
                applyDownloadDateFilter(query, group.propertyMin, group.propertyMax);
            else // Direct link to group
                query.in(Content_.id, selectFilteredContent(groupId));
        }
        applySortOrder(query, orderField, orderDesc);
        return query.build();
    }

    long[] selectContentSearchContentByGroupItem(
            String title,
            long groupId,
            List<Attribute> metadata,
            boolean filterFavourites,
            int orderField,
            boolean orderDesc,
            boolean bookCompletedOnly,
            boolean bookNotCompletedOnly) {
        if (orderField != Preferences.Constant.ORDER_FIELD_CUSTOM) return new long[]{};

        AttributeMap metadataMap = new AttributeMap();
        metadataMap.addAll(metadata);

        boolean hasTitleFilter = (title != null && title.length() > 0);
        boolean hasGroupFilter = (groupId > 0);
        List<Attribute> sources = metadataMap.get(AttributeType.SOURCE);
        boolean hasSiteFilter = metadataMap.containsKey(AttributeType.SOURCE)
                && (sources != null)
                && !(sources.isEmpty());
        boolean hasTagFilter = metadataMap.keySet().size() > (hasSiteFilter ? 1 : 0);

        // Pre-filter and order on GroupItem
        QueryBuilder<GroupItem> query = store.boxFor(GroupItem.class).query();
        if (hasGroupFilter) query.equal(GroupItem_.groupId, groupId);

        if (orderDesc) query.orderDesc(GroupItem_.order);
        else query.order(GroupItem_.order);

        // Get linked Content
        QueryBuilder<Content> contentQuery = query.link(GroupItem_.content);
        if (hasSiteFilter)
            contentQuery.in(Content_.site, getIdsFromAttributes(sources));
        if (filterFavourites) contentQuery.equal(Content_.favourite, true);

        if (bookCompletedOnly) contentQuery.equal(Content_.completed, true);
        else if (bookNotCompletedOnly) contentQuery.equal(Content_.completed, false);

        if (hasTitleFilter)
            contentQuery.contains(Content_.title, title, QueryBuilder.StringOrder.CASE_INSENSITIVE);
        if (hasTagFilter) {
            for (Map.Entry<AttributeType, List<Attribute>> entry : metadataMap.entrySet()) {
                AttributeType attrType = entry.getKey();
                if (!attrType.equals(AttributeType.SOURCE)) { // Not a "real" attribute in database
                    List<Attribute> attrs = entry.getValue();
                    if (attrs != null && !attrs.isEmpty()) {
                        contentQuery.in(Content_.id, selectFilteredContent(attrs, false, false, false));
                    }
                }
            }
        }
        return Helper.getPrimitiveLongArrayFromList(Stream.of(query.build().find()).map(gi -> gi.content.getTargetId()).toList());
    }

    private Query<Content> selectContentUniversalAttributesQ(
            String queryStr,
            long groupId,
            boolean filterBookFavourites,
            boolean filterPageFavourites,
            boolean bookCompletedOnly,
            boolean bookNotCompletedOnly,
            int[] statuses) {
        QueryBuilder<Content> query = store.boxFor(Content.class).query();
        query.in(Content_.status, statuses);

        if (filterBookFavourites) query.equal(Content_.favourite, true);

        if (bookCompletedOnly) query.equal(Content_.completed, true);
        else if (bookNotCompletedOnly) query.equal(Content_.completed, false);

        if (filterPageFavourites) filterWithPageFavs(query);
        query.link(Content_.attributes).contains(Attribute_.name, queryStr, QueryBuilder.StringOrder.CASE_INSENSITIVE);

        if (groupId > 0) query.in(Content_.id, selectFilteredContent(groupId));

        return query.build();
    }

    private Query<Content> selectContentUniversalContentQ(
            String queryStr,
            long groupId,
            boolean filterBookFavourites,
            boolean filterPageFavourites,
            long[] additionalIds,
            int orderField,
            boolean orderDesc,
            boolean bookCompletedOnly,
            boolean bookNotCompletedOnly,
            int[] statuses) {
        if (Preferences.Constant.ORDER_FIELD_CUSTOM == orderField)
            return store.boxFor(Content.class).query().build();

        QueryBuilder<Content> query = store.boxFor(Content.class).query();
        query.in(Content_.status, statuses);

        if (filterBookFavourites) query.equal(Content_.favourite, true);

        if (bookCompletedOnly) query.equal(Content_.completed, true);
        else if (bookNotCompletedOnly) query.equal(Content_.completed, false);

        if (filterPageFavourites) filterWithPageFavs(query);

        query.contains(Content_.title, queryStr, QueryBuilder.StringOrder.CASE_INSENSITIVE);
        query.or().equal(Content_.uniqueSiteId, queryStr, QueryBuilder.StringOrder.CASE_INSENSITIVE);
        //        query.or().link(Content_.attributes).contains(Attribute_.name, queryStr, QueryBuilder.StringOrder.CASE_INSENSITIVE); // Use of or() here is not possible yet with ObjectBox v2.3.1
        query.or().in(Content_.id, additionalIds);

        if (groupId > 0) {
            Group group = store.boxFor(Group.class).get(groupId);
            if (group.grouping.equals(Grouping.DL_DATE)) // According to days since download date
                applyDownloadDateFilter(query, group.propertyMin, group.propertyMax);
            else // Direct link to group
                query.in(Content_.id, selectFilteredContent(groupId));
        }

        applySortOrder(query, orderField, orderDesc);

        return query.build();
    }

    private long[] selectContentUniversalContentByGroupItem(
            String queryStr,
            long groupId,
            boolean filterFavourites,
            long[] additionalIds,
            int orderField,
            boolean orderDesc,
            boolean bookCompletedOnly,
            boolean bookNotCompletedOnly) {
        if (orderField != Preferences.Constant.ORDER_FIELD_CUSTOM) return new long[]{};

        // Pre-filter and order on GroupItem
        QueryBuilder<GroupItem> query = store.boxFor(GroupItem.class).query();
        if (groupId > 0) query.equal(GroupItem_.groupId, groupId);
        if (orderDesc) query.orderDesc(GroupItem_.order);
        else query.order(GroupItem_.order);

        // Get linked content
        QueryBuilder<Content> contentQuery = query.link(GroupItem_.content);
        contentQuery.in(Content_.status, libraryStatus);

        if (filterFavourites) contentQuery.equal(Content_.favourite, true);

        if (bookCompletedOnly) contentQuery.equal(Content_.completed, true);
        else if (bookNotCompletedOnly) contentQuery.equal(Content_.completed, false);

        contentQuery.contains(Content_.title, queryStr, QueryBuilder.StringOrder.CASE_INSENSITIVE);
        contentQuery.or().equal(Content_.uniqueSiteId, queryStr, QueryBuilder.StringOrder.CASE_INSENSITIVE);
        //        query.or().link(Content_.attributes).contains(Attribute_.name, queryStr, QueryBuilder.StringOrder.CASE_INSENSITIVE); // Use of or() here is not possible yet with ObjectBox v2.3.1
        contentQuery.or().in(Content_.id, additionalIds);
        if (groupId > 0) contentQuery.in(Content_.id, selectFilteredContent(groupId));

        return Helper.getPrimitiveLongArrayFromList(Stream.of(query.build().find()).map(gi -> gi.content.getTargetId()).toList());
    }

    Query<Content> selectContentUniversalQ(
            String queryStr,
            long groupId,
            boolean filterBookFavourites,
            boolean filterPageFavourites,
            int orderField,
            boolean orderDesc,
            boolean bookCompletedOnly,
            boolean bookNotCompletedOnly) {
        // Due to objectBox limitations (see https://github.com/objectbox/objectbox-java/issues/497 and https://github.com/objectbox/objectbox-java/issues/201)
        // querying Content and attributes have to be done separately
        Query<Content> contentAttrSubQuery = selectContentUniversalAttributesQ(queryStr, groupId, filterBookFavourites, filterPageFavourites, bookCompletedOnly, bookNotCompletedOnly, libraryStatus);
        return selectContentUniversalContentQ(queryStr, groupId, filterBookFavourites, filterPageFavourites, contentAttrSubQuery.findIds(), orderField, orderDesc, bookCompletedOnly, bookNotCompletedOnly, libraryStatus);
    }

    long[] selectContentUniversalByGroupItem(
            String queryStr,
            long groupId,
            boolean filterBookFavourites,
            boolean filterPageFavourites,
            int orderField,
            boolean orderDesc,
            boolean bookCompletedOnly,
            boolean bookNotCompletedOnly) {
        // Due to objectBox limitations (see https://github.com/objectbox/objectbox-java/issues/497 and https://github.com/objectbox/objectbox-java/issues/201)
        // querying Content and attributes have to be done separately
        Query<Content> contentAttrSubQuery = selectContentUniversalAttributesQ(queryStr, groupId, filterBookFavourites, filterPageFavourites, bookCompletedOnly, bookNotCompletedOnly, libraryStatus);
        return selectContentUniversalContentByGroupItem(queryStr, groupId, filterBookFavourites, contentAttrSubQuery.findIds(), orderField, orderDesc, bookCompletedOnly, bookNotCompletedOnly);
    }

    public List<Long> getShuffledIds() {
        return Stream.of(store.boxFor(ShuffleRecord.class).getAll()).map(ShuffleRecord::getContentId).toList();
    }

    public void shuffleContentIds() {
        // Clear previous shuffled list
        Box<ShuffleRecord> shuffleStore = store.boxFor(ShuffleRecord.class);
        shuffleStore.removeAll();
        // Populate with a new list
        List<Long> allBooksIds = Helper.getListFromPrimitiveArray(selectStoredContentQ(false, false, -1, false).build().findIds());
        Collections.shuffle(allBooksIds, new Random(RandomSeedSingleton.getInstance().getSeed(Consts.SEED_CONTENT)));
        shuffleStore.put(Stream.of(allBooksIds).map(ShuffleRecord::new).toList());
    }

    private long[] shuffleRandomSortId(Query<Content> query) {
        List<Long> queryIds = Helper.getListFromPrimitiveArray(query.findIds());
        List<Long> shuffleIds = getShuffledIds();

        // Keep common IDs
        shuffleIds.retainAll(queryIds);

        // Isolate new IDs that have never been shuffled and append them at the end
        if (shuffleIds.size() < queryIds.size()) {
            queryIds.removeAll(shuffleIds);
            shuffleIds.addAll(queryIds);
        }

        return Helper.getPrimitiveLongArrayFromList(shuffleIds);
    }

    long[] selectContentSearchId(String title, long groupId, List<Attribute> tags, boolean filterBookFavourites, boolean filterPageFavourites, int orderField, boolean orderDesc, boolean bookCompletedOnly, boolean bookNotCompletedOnly) {
        long[] result;
        Query<Content> query = selectContentSearchContentQ(title, groupId, tags, filterBookFavourites, filterPageFavourites, orderField, orderDesc, bookCompletedOnly, bookNotCompletedOnly);

        if (orderField != Preferences.Constant.ORDER_FIELD_RANDOM) {
            result = query.findIds();
        } else {
            result = shuffleRandomSortId(query);
        }
        return result;
    }

    long[] selectContentUniversalId(
            String queryStr,
            long groupId,
            boolean filterBookFavourites,
            boolean filterPageFavourites,
            int orderField,
            boolean orderDesc,
            boolean bookCompletedOnly,
            boolean bookNotCompletedOnly,
            int[] statuses) {
        long[] result;
        // Due to objectBox limitations (see https://github.com/objectbox/objectbox-java/issues/497 and https://github.com/objectbox/objectbox-java/issues/201)
        // querying Content and attributes have to be done separately
        Query<Content> contentAttrSubQuery = selectContentUniversalAttributesQ(queryStr, groupId, filterBookFavourites, filterPageFavourites, bookCompletedOnly, bookNotCompletedOnly, statuses);
        Query<Content> query = selectContentUniversalContentQ(queryStr, groupId, filterBookFavourites, filterPageFavourites, contentAttrSubQuery.findIds(), orderField, orderDesc, bookCompletedOnly, bookNotCompletedOnly, statuses);

        if (orderField != Preferences.Constant.ORDER_FIELD_RANDOM) {
            result = query.findIds();
        } else {
            result = shuffleRandomSortId(query);
        }
        return result;
    }

    private long[] selectFilteredContent(long groupId) {
        if (groupId < 1) return new long[0];

        Box<Group> box = store.boxFor(Group.class);
        Group group = box.get(groupId);
        if (null == group) return new long[0];

        return Helper.getPrimitiveLongArrayFromList(group.getContentIds());
    }

    private long[] selectFilteredContent(List<Attribute> attrs, boolean filterFavourites, boolean bookCompletedOnly, boolean bookNotCompletedOnly) {
        if (null == attrs || attrs.isEmpty()) return new long[0];

        // Pre-build queries to reuse them efficiently within the loops
        QueryBuilder<Content> contentFromSourceQueryBuilder = store.boxFor(Content.class).query();
        contentFromSourceQueryBuilder.in(Content_.status, libraryStatus);
        contentFromSourceQueryBuilder.equal(Content_.site, 1);
        if (filterFavourites) contentFromSourceQueryBuilder.equal(Content_.favourite, true);

        if (bookCompletedOnly) contentFromSourceQueryBuilder.equal(Content_.completed, true);
        else if (bookNotCompletedOnly)
            contentFromSourceQueryBuilder.equal(Content_.completed, false);

        Query<Content> contentFromSourceQuery = contentFromSourceQueryBuilder.build();

        QueryBuilder<Content> contentFromAttributesQueryBuilder = store.boxFor(Content.class).query();
        contentFromAttributesQueryBuilder.in(Content_.status, libraryStatus);
        if (filterFavourites) contentFromAttributesQueryBuilder.equal(Content_.favourite, true);

        // If first tag is to be excluded, start shrinking the whole database
        List<Long> idsFull = attrs.get(0).isExcluded()
                ? Helper.getListFromPrimitiveArray(contentFromAttributesQueryBuilder.build().findIds())
                : Collections.emptyList();

        contentFromAttributesQueryBuilder = store.boxFor(Content.class).query();
        contentFromAttributesQueryBuilder.in(Content_.status, libraryStatus);
        if (filterFavourites) contentFromAttributesQueryBuilder.equal(Content_.favourite, true);

        if (bookCompletedOnly) contentFromAttributesQueryBuilder.equal(Content_.completed, true);
        else if (bookNotCompletedOnly)
            contentFromAttributesQueryBuilder.equal(Content_.completed, false);

        contentFromAttributesQueryBuilder.link(Content_.attributes)
                .equal(Attribute_.type, 0)
                .equal(Attribute_.name, "", QueryBuilder.StringOrder.CASE_INSENSITIVE);
        Query<Content> contentFromAttributesQuery = contentFromAttributesQueryBuilder.build();

        // Cumulative query loop
        // Each iteration restricts the results of the next because advanced search uses an AND logic
        List<Long> results = Collections.emptyList();
        long[] ids;

        for (Attribute attr : attrs) {
            if (attr.getType().equals(AttributeType.SOURCE)) {
                ids = contentFromSourceQuery.setParameter(Content_.site, attr.getId()).findIds();
            } else {
                ids = contentFromAttributesQuery.setParameter(Attribute_.type, attr.getType().getCode())
                        .setParameter(Attribute_.name, attr.getName()).findIds();
            }
            if (results.isEmpty()) { // First iteration
                results = Helper.getListFromPrimitiveArray(ids);

                // If first tag is to be excluded, start trimming results
                if (attr.isExcluded()) {
                    idsFull.removeAll(results);
                    results = idsFull;
                }
            } else {
                // Filter results with newly found IDs (only common IDs should stay)
                List<Long> idsAsList = Helper.getListFromPrimitiveArray(ids);
                // Remove ids that fit the attribute from results
                if (attr.isExcluded())
                    results.removeAll(idsAsList);
                else
                    results.retainAll(idsAsList);
            }
        }

        return Helper.getPrimitiveLongArrayFromList(results);
    }

    private void filterWithPageFavs(QueryBuilder<Content> builder) {
        builder.link(Content_.imageFiles).equal(ImageFile_.favourite, true);
    }

    List<Attribute> selectAvailableSources() {
        return selectAvailableSources(null);
    }

    List<Attribute> selectAvailableSources(List<Attribute> filter) {
        List<Attribute> result = new ArrayList<>();

        QueryBuilder<Content> query = store.boxFor(Content.class).query();
        query.in(Content_.status, libraryStatus);

        if (filter != null && !filter.isEmpty()) {
            AttributeMap metadataMap = new AttributeMap();
            metadataMap.addAll(filter);

            List<Attribute> params = metadataMap.get(AttributeType.SOURCE);
            if (params != null && !params.isEmpty())
                query.in(Content_.site, getIdsFromAttributes(params));

            for (Map.Entry<AttributeType, List<Attribute>> entry : metadataMap.entrySet()) {
                AttributeType attrType = entry.getKey();
                if (!attrType.equals(AttributeType.SOURCE)) { // Not a "real" attribute in database
                    List<Attribute> attrs = entry.getValue();
                    if (attrs != null && !attrs.isEmpty())
                        query.in(Content_.id, selectFilteredContent(attrs, false, false, false));
                }
            }

        }

        List<Content> content = query.build().find();

        // SELECT field, COUNT(*) GROUP BY (field) is not implemented in ObjectBox v2.3.1
        // (see https://github.com/objectbox/objectbox-java/issues/422)
        // => Group by and count have to be done manually (thanks God Stream exists !)
        // Group and count by source
        Map<Site, List<Content>> map = Stream.of(content).collect(Collectors.groupingBy(Content::getSite));
        for (Map.Entry<Site, List<Content>> entry : map.entrySet()) {
            Site site = entry.getKey();
            int size = (null == entry.getValue()) ? 0 : entry.getValue().size();
            result.add(new Attribute(AttributeType.SOURCE, site.getDescription()).setExternalId(site.getCode()).setCount(size));
        }
        // Order by count desc
        result = Stream.of(result).sortBy(a -> -a.getCount()).collect(toList());

        return result;
    }

    Query<Content> selectErrorContentQ() {
        return store.boxFor(Content.class).query().equal(Content_.status, StatusContent.ERROR.getCode()).orderDesc(Content_.downloadDate).build();
    }

    List<Content> selectContentByDlDate(int minDays, int maxDays) {
        QueryBuilder<Content> qb = store.boxFor(Content.class).query();
        qb.in(Content_.status, libraryStatus);
        applyDownloadDateFilter(qb, minDays, maxDays);
        return qb.build().find();
    }

    private void applyDownloadDateFilter(@NonNull final QueryBuilder<Content> qb, int minDays, int maxDays) {
        long today = Instant.now().toEpochMilli();
        long minDownloadDate = today - (maxDays * DAY_IN_MILLIS);
        long maxDownloadDate = today - (minDays * DAY_IN_MILLIS);
        qb.between(Content_.downloadDate, minDownloadDate, maxDownloadDate);
    }

    private Query<Attribute> queryAvailableAttributes(
            @NonNull final AttributeType type,
            String filter,
            long[] filteredContent) {
        QueryBuilder<Attribute> query = store.boxFor(Attribute.class).query();
        query.equal(Attribute_.type, type.getCode());
        if (filter != null && !filter.trim().isEmpty())
            query.contains(Attribute_.name, filter.trim(), QueryBuilder.StringOrder.CASE_INSENSITIVE);
        if (filteredContent.length > 0)
            query.link(Attribute_.contents).in(Content_.id, filteredContent).in(Content_.status, libraryStatus);
        else
            query.link(Attribute_.contents).in(Content_.status, libraryStatus);

        return query.build();
    }

    long countAvailableAttributes(AttributeType
                                          type, List<Attribute> attributeFilter, String filter, boolean filterFavourites, boolean bookCompletedOnly, boolean bookNotCompletedOnly) {
        return queryAvailableAttributes(type, filter, selectFilteredContent(attributeFilter, filterFavourites, bookCompletedOnly, bookNotCompletedOnly)).count();
    }

    @SuppressWarnings("squid:S2184")
        // In our case, limit() argument has to be human-readable -> no issue concerning its type staying in the int range
    List<Attribute> selectAvailableAttributes(
            @NonNull AttributeType type,
            List<Attribute> attributeFilter,
            String filter,
            boolean filterFavourites,
            int sortOrder,
            int page,
            int itemsPerPage,
            boolean bookCompletedOnly,
            boolean bookNotCompletedOnly) {
        long[] filteredContent = selectFilteredContent(attributeFilter, filterFavourites, bookCompletedOnly, bookNotCompletedOnly);
        if (filteredContent.length == 0 && attributeFilter != null && !attributeFilter.isEmpty())
            return Collections.emptyList();
        List<Long> filteredContentAsList = Helper.getListFromPrimitiveArray(filteredContent);
        List<Integer> libraryStatusAsList = Helper.getListFromPrimitiveArray(libraryStatus);
        List<Attribute> result = queryAvailableAttributes(type, filter, filteredContent).find();

        // Compute attribute count for sorting
        long count;
        for (Attribute a : result) {
            count = Stream.of(a.contents)
                    .filter(c -> libraryStatusAsList.contains(c.getStatus().getCode()))
                    .filter(c -> filteredContentAsList.isEmpty() || filteredContentAsList.contains(c.getId()))
                    .count();
            a.setCount((int) count);
        }

        // Apply sort order
        Stream<Attribute> s = Stream.of(result);
        if (Preferences.Constant.ORDER_ATTRIBUTES_ALPHABETIC == sortOrder) {
            s = s.sortBy(a -> -a.getCount()).sortBy(Attribute::getName);
        } else {
            s = s.sortBy(Attribute::getName).sortBy(a -> -a.getCount());
        }

        // Apply paging
        if (itemsPerPage > 0) {
            int start = (page - 1) * itemsPerPage;
            s = s.limit(page * itemsPerPage).skip(start); // squid:S2184 here because int * int -> int (not long)
        }
        return s.collect(toList());
    }

    SparseIntArray countAvailableAttributesPerType() {
        return countAvailableAttributesPerType(null);
    }

    SparseIntArray countAvailableAttributesPerType(List<Attribute> attributeFilter) {
        // Get Content filtered by current selection
        long[] filteredContent = selectFilteredContent(attributeFilter, false, false, false);
        // Get available attributes of the resulting content list
        QueryBuilder<Attribute> query = store.boxFor(Attribute.class).query();

        if (filteredContent.length > 0)
            query.link(Attribute_.contents).in(Content_.id, filteredContent).in(Content_.status, libraryStatus);
        else
            query.link(Attribute_.contents).in(Content_.status, libraryStatus);

        List<Attribute> attributes = query.build().find();

        SparseIntArray result = new SparseIntArray();
        // SELECT field, COUNT(*) GROUP BY (field) is not implemented in ObjectBox v2.3.1
        // (see https://github.com/objectbox/objectbox-java/issues/422)
        // => Group by and count have to be done manually (thanks God Stream exists !)
        // Group and count by type
        Map<AttributeType, List<Attribute>> map = Stream.of(attributes).collect(Collectors.groupingBy(Attribute::getType));

        for (Map.Entry<AttributeType, List<Attribute>> entry : map.entrySet()) {
            AttributeType t = entry.getKey();
            if (filteredContent.length == 0 && attributeFilter != null)
                result.append(t.getCode(), 0);
            else {
                int size = (null == entry.getValue()) ? 0 : entry.getValue().size();
                result.append(t.getCode(), size);
            }
        }

        return result;
    }

    List<Content> selectContentWithTitle(@NonNull String word, int[] contentStatusCodes) {
        QueryBuilder<Content> query = store.boxFor(Content.class).query();
        query.contains(Content_.title, word, QueryBuilder.StringOrder.CASE_INSENSITIVE);
        query.in(Content_.status, contentStatusCodes);
        return query.build().find();
    }

    void updateImageFileStatusParamsMimeTypeUriSize(@NonNull ImageFile image) {
        Box<ImageFile> imgBox = store.boxFor(ImageFile.class);
        ImageFile img = imgBox.get(image.getId());
        if (img != null) {
            img.setStatus(image.getStatus());
            img.setDownloadParams(image.getDownloadParams());
            img.setMimeType(image.getMimeType());
            img.setFileUri(image.getFileUri());
            img.setSize(image.getSize());
            imgBox.put(img);
        }
    }

    void updateImageContentStatus(
            long contentId,
            @Nullable StatusContent updateFrom,
            @NonNull StatusContent updateTo) {
        QueryBuilder<ImageFile> query = store.boxFor(ImageFile.class).query();
        if (updateFrom != null) query.equal(ImageFile_.status, updateFrom.getCode());
        List<ImageFile> imgs = query.equal(ImageFile_.contentId, contentId).build().find();
        if (imgs.isEmpty()) return;

        for (ImageFile img : imgs) img.setStatus(updateTo);
        store.boxFor(ImageFile.class).put(imgs);
    }

    void updateImageFileUrl(@NonNull final ImageFile image) {
        Box<ImageFile> imgBox = store.boxFor(ImageFile.class);
        ImageFile img = imgBox.get(image.getId());
        if (img != null) {
            img.setUrl(image.getUrl());
            imgBox.put(img);
        }
    }

    // Returns a list of processed images grouped by status, with count and filesize (in bytes)
    Map<StatusContent, ImmutablePair<Integer, Long>> countProcessedImagesById(long contentId) {
        QueryBuilder<ImageFile> imgQuery = store.boxFor(ImageFile.class).query();
        imgQuery.equal(ImageFile_.contentId, contentId);
        List<ImageFile> images = imgQuery.build().find();

        Map<StatusContent, ImmutablePair<Integer, Long>> result = new EnumMap<>(StatusContent.class);
        // SELECT field, COUNT(*) GROUP BY (field) is not implemented in ObjectBox v2.3.1
        // (see https://github.com/objectbox/objectbox-java/issues/422)
        // => Group by and count have to be done manually (thanks God Stream exists !)
        // Group and count by type
        Map<StatusContent, List<ImageFile>> map = Stream.of(images).collect(Collectors.groupingBy(ImageFile::getStatus));
        for (Map.Entry<StatusContent, List<ImageFile>> entry : map.entrySet()) {
            StatusContent t = entry.getKey();
            int count = 0;
            long sizeBytes = 0;
            if (entry.getValue() != null) {
                count = entry.getValue().size();
                for (ImageFile img : entry.getValue()) sizeBytes += img.getSize();
            }
            result.put(t, new ImmutablePair<>(count, sizeBytes));
        }

        return result;
    }

    Map<Site, ImmutablePair<Integer, Long>> selectPrimaryMemoryUsagePerSource() {
        return selectMemoryUsagePerSource(new int[]{StatusContent.DOWNLOADED.getCode(), StatusContent.MIGRATED.getCode()});
    }

    Map<Site, ImmutablePair<Integer, Long>> selectExternalMemoryUsagePerSource() {
        return selectMemoryUsagePerSource(new int[]{StatusContent.EXTERNAL.getCode()});
    }

    Map<Site, ImmutablePair<Integer, Long>> selectMemoryUsagePerSource(int[] statusCodes) {
        // Get all downloaded images regardless of the book's status
        QueryBuilder<Content> query = store.boxFor(Content.class).query();
        query.in(Content_.status, statusCodes);
        List<Content> books = query.build().find();

        Map<Site, ImmutablePair<Integer, Long>> result = new EnumMap<>(Site.class);
        // SELECT field, COUNT(*) GROUP BY (field) is not implemented in ObjectBox v2.3.1
        // (see https://github.com/objectbox/objectbox-java/issues/422)
        // => Group by and count have to be done manually (thanks God Stream exists !)
        // Group and count by type
        Map<Site, List<Content>> map = Stream.of(books).collect(Collectors.groupingBy(Content::getSite));
        for (Map.Entry<Site, List<Content>> entry : map.entrySet()) {
            Site s = entry.getKey();
            int count = 0;
            long size = 0;
            if (entry.getValue() != null) {
                count = entry.getValue().size();
                for (Content c : entry.getValue()) size += c.getSize();
            }
            result.put(s, new ImmutablePair<>(count, size));
        }

        return result;
    }

    void insertErrorRecord(@NonNull final ErrorRecord record) {
        store.boxFor(ErrorRecord.class).put(record);
    }

    List<ErrorRecord> selectErrorRecordByContentId(long contentId) {
        return store.boxFor(ErrorRecord.class).query().equal(ErrorRecord_.contentId, contentId).build().find();
    }

    void deleteErrorRecords(long contentId) {
        List<ErrorRecord> records = selectErrorRecordByContentId(contentId);
        store.boxFor(ErrorRecord.class).remove(records);
    }

    void insertImageFile(@NonNull ImageFile img) {
        if (img.getId() > 0) store.boxFor(ImageFile.class).put(img);
    }

    void deleteImageFiles(long contentId) {
        store.boxFor(ImageFile.class).query().equal(ImageFile_.contentId, contentId).build().remove();
    }

    void deleteImageFiles(List<ImageFile> images) {
        store.boxFor(ImageFile.class).remove(images);
    }

    void insertImageFiles(@NonNull List<ImageFile> imgs) {
        store.boxFor(ImageFile.class).put(imgs);
    }

    void replaceImageFiles(long contentId, @NonNull final List<ImageFile> newList) {
        store.runInTx(() -> {
            deleteImageFiles(contentId);
            for (ImageFile img : newList) img.setContentId(contentId);
            insertImageFiles(newList);
        });
    }

    @Nullable
    ImageFile selectImageFile(long id) {
        if (id > 0) return store.boxFor(ImageFile.class).get(id);
        else return null;
    }

    Query<ImageFile> selectDownloadedImagesFromContentQ(long id) {
        QueryBuilder<ImageFile> builder = store.boxFor(ImageFile.class).query();
        builder.equal(ImageFile_.contentId, id);
        builder.in(ImageFile_.status, new int[]{StatusContent.DOWNLOADED.getCode(), StatusContent.EXTERNAL.getCode(), StatusContent.ONLINE.getCode()});
        builder.order(ImageFile_.order);
        return builder.build();
    }

    void insertSiteHistory(@NonNull Site site, @NonNull String url) {
        SiteHistory siteHistory = selectHistory(site);
        if (siteHistory != null) {
            siteHistory.setUrl(url);
            store.boxFor(SiteHistory.class).put(siteHistory);
        } else {
            store.boxFor(SiteHistory.class).put(new SiteHistory(site, url));
        }
    }

    @Nullable
    SiteHistory selectHistory(@NonNull Site s) {
        return store.boxFor(SiteHistory.class).query().equal(SiteHistory_.site, s.getCode()).build().findFirst();
    }

    Query<SiteBookmark> selectBookmarksQ(@Nullable Site s) {
        QueryBuilder<SiteBookmark> qb = store.boxFor(SiteBookmark.class).query();
        if (s != null) qb.equal(SiteBookmark_.site, s.getCode());

        return qb.order(SiteBookmark_.order).build();
    }

    String[] selectAllBooksmarkUrls() {
        return store.boxFor(SiteBookmark.class).query().build().property(SiteBookmark_.url).findStrings();
    }

    long insertBookmark(@NonNull final SiteBookmark bookmark) {
        return store.boxFor(SiteBookmark.class).put(bookmark);
    }

    void insertBookmarks(@NonNull final List<SiteBookmark> bookmarks) {
        store.boxFor(SiteBookmark.class).put(bookmarks);
    }

    void deleteBookmark(long bookmarkId) {
        store.boxFor(SiteBookmark.class).remove(bookmarkId);
    }

    int getMaxBookmarkOrderFor(@NonNull final Site site) {
        return (int) store.boxFor(SiteBookmark.class).query().equal(SiteBookmark_.site, site.getCode()).build().property(SiteBookmark_.order).max();
    }

    long insertGroup(Group group) {
        return store.boxFor(Group.class).put(group);
    }

    long insertGroupItem(GroupItem item) {
        return store.boxFor(GroupItem.class).put(item);
    }

    List<GroupItem> selectGroupItems(long[] groupItemIds) {
        return store.boxFor(GroupItem.class).get(groupItemIds);
    }

    List<GroupItem> selectGroupItems(long contentId, int groupingId) {
        QueryBuilder<GroupItem> qb = store.boxFor(GroupItem.class).query().equal(GroupItem_.contentId, contentId);
        qb.link(GroupItem_.group).equal(Group_.grouping, groupingId);
        return qb.build().find();
    }

    void deleteGroupItem(long groupItemId) {
        store.boxFor(GroupItem.class).remove(groupItemId);
    }

    void deleteGroupItems(long[] groupItemIds) {
        store.boxFor(GroupItem.class).remove(groupItemIds);
    }

    long countGroupsFor(@NonNull final Grouping grouping) {
        return store.boxFor(Group.class).query().equal(Group_.grouping, grouping.getId()).build().count();
    }

    int getMaxGroupOrderFor(@NonNull final Grouping grouping) {
        return (int) store.boxFor(Group.class).query().equal(Group_.grouping, grouping.getId()).build().property(Group_.order).max();
    }

    int getMaxGroupItemOrderFor(long groupid) {
        return (int) store.boxFor(GroupItem.class).query().equal(GroupItem_.groupId, groupid).build().property(GroupItem_.order).max();
    }

    Query<Group> selectGroupsQ(
            int grouping,
            @Nullable String query,
            int orderField,
            boolean orderDesc,
            int artistGroupVisibility,
            boolean groupFavouritesOnly) {
        QueryBuilder<Group> qb = store.boxFor(Group.class).query().equal(Group_.grouping, grouping);
        if (query != null)
            qb.contains(Group_.name, query, QueryBuilder.StringOrder.CASE_INSENSITIVE);

        if (grouping == Grouping.ARTIST.getId() && artistGroupVisibility != Preferences.Constant.ARTIST_GROUP_VISIBILITY_ARTISTS_GROUPS)
            qb.equal(Group_.subtype, artistGroupVisibility);

        if (groupFavouritesOnly) qb.equal(Group_.favourite, true);

        Property<Group> property = Group_.name;
        if (Preferences.Constant.ORDER_FIELD_CUSTOM == orderField || grouping == Grouping.DL_DATE.getId())
            property = Group_.order;
        // Order by number of children is done by the DAO

        if (orderDesc) qb.orderDesc(property);
        else qb.order(property);

        return qb.build();
    }

    @Nullable
    Group selectGroup(long groupId) {
        return store.boxFor(Group.class).get(groupId);
    }

    @Nullable
    List<Group> selectGroups(long[] groupIds) {
        return store.boxFor(Group.class).get(groupIds);
    }

    @Nullable
    Group selectGroupByName(int grouping, @NonNull final String name) {
        return store.boxFor(Group.class).query().equal(Group_.grouping, grouping).equal(Group_.name, name, QueryBuilder.StringOrder.CASE_INSENSITIVE).build().findFirst();
    }

    void deleteGroup(long groupId) {
        store.boxFor(Group.class).remove(groupId);
    }

    Query<Group> selectGroupsByGroupingQ(int groupingId) {
        return store.boxFor(Group.class).query().equal(Group_.grouping, groupingId).build();
    }

    Query<Group> selectFlaggedGroupsQ() {
        return store.boxFor(Group.class).query().equal(Group_.isFlaggedForDeletion, true).build();
    }

    void flagGroups(List<Group> groupList, boolean flag) {
        for (Group g : groupList) g.setFlaggedForDeletion(flag);
        store.boxFor(Group.class).put(groupList);
    }

    void deleteGroupItemsByGrouping(int groupingId) {
        QueryBuilder<GroupItem> qb = store.boxFor(GroupItem.class).query();
        qb.link(GroupItem_.group).equal(Group_.grouping, groupingId);
        qb.build().remove();
    }

    void deleteGroupItemsByGroup(long groupId) {
        QueryBuilder<GroupItem> qb = store.boxFor(GroupItem.class).query();
        qb.link(GroupItem_.group).equal(Group_.id, groupId);
        qb.build().remove();
    }

    List<Chapter> selectChapters(long contentId) {
        return store.boxFor(Chapter.class).query().equal(Chapter_.contentId, contentId).order(Chapter_.order).build().find();
    }

    void insertChapters(List<Chapter> chapters) {
        store.boxFor(Chapter.class).put(chapters);
    }

    void deleteChaptersByContentId(long contentId) {
        QueryBuilder<Chapter> qb = store.boxFor(Chapter.class).query();
        qb.equal(Chapter_.contentId, contentId);
        qb.build().remove();
    }

    void deleteChapter(long chapterId) {
        store.boxFor(Chapter.class).remove(chapterId);
    }

    /**
     * ONE-SHOT USE QUERIES (MIGRATION & MAINTENANCE)
     */

    List<Content> selectContentWithOldPururinHost() {
        return store.boxFor(Content.class).query().contains(Content_.coverImageUrl, "://api.pururin.io/images/", QueryBuilder.StringOrder.CASE_INSENSITIVE).build().find();
    }

    List<Content> selectContentWithOldTsuminoCovers() {
        return store.boxFor(Content.class).query().contains(Content_.coverImageUrl, "://www.tsumino.com/Image/Thumb/", QueryBuilder.StringOrder.CASE_INSENSITIVE).build().find();
    }

    List<Content> selectDownloadedContentWithNoSize() {
        return store.boxFor(Content.class).query().in(Content_.status, libraryStatus).isNull(Content_.size).build().find();
    }

    List<Content> selectDownloadedContentWithNoReadProgress() {
        return store.boxFor(Content.class).query().in(Content_.status, libraryStatus).isNull(Content_.readProgress).build().find();
    }

    List<Content> selectContentWithNullCompleteField() {
        return store.boxFor(Content.class).query().isNull(Content_.completed).build().find();
    }

    List<Content> selectContentWithNullDlModeField() {
        return store.boxFor(Content.class).query().isNull(Content_.downloadMode).build().find();
    }

    List<Content> selectContentWithNullMergeField() {
        return store.boxFor(Content.class).query().isNull(Content_.manuallyMerged).build().find();
    }

    public Query<Content> selectOldStoredContentQ() {
        QueryBuilder<Content> query = store.boxFor(Content.class).query();
        query.in(Content_.status, new int[]{
                StatusContent.DOWNLOADING.getCode(),
                StatusContent.PAUSED.getCode(),
                StatusContent.ERROR.getCode(),
                StatusContent.DOWNLOADED.getCode(),
                StatusContent.MIGRATED.getCode()});
        query.notNull(Content_.storageFolder);
        query.notEqual(Content_.storageFolder, "", QueryBuilder.StringOrder.CASE_INSENSITIVE);
        return query.build();
    }

    QueryBuilder<Content> selectStoredContentQ(boolean nonFavouritesOnly, boolean includeQueued, int orderField, boolean orderDesc) {
        QueryBuilder<Content> query = store.boxFor(Content.class).query();
        if (includeQueued)
            query.in(Content_.status, libraryQueueStatus);
        else
            query.in(Content_.status, libraryStatus);
        query.notNull(Content_.storageUri);
        query.notEqual(Content_.storageUri, "", QueryBuilder.StringOrder.CASE_INSENSITIVE);
        if (nonFavouritesOnly) query.equal(Content_.favourite, false);
        if (orderField > -1) {
            Property<Content> field = getPropertyFromField(orderField);
            if (null != field) {
                if (orderDesc) query.orderDesc(field);
                else query.order(field);
            }
        }
        return query;
    }

    Query<Content> selectNonHashedContent() {
        QueryBuilder<Content> query = store.boxFor(Content.class).query()
                .in(Content_.status, new int[]{
                        StatusContent.DOWNLOADED.getCode(),
                        StatusContent.MIGRATED.getCode()
                })
                .notNull(Content_.storageUri)
                .notEqual(Content_.storageUri, "", QueryBuilder.StringOrder.CASE_INSENSITIVE);

        QueryBuilder<ImageFile> imageQuery = query.backlink(ImageFile_.content);
        imageQuery.equal(ImageFile_.isCover, true)
                .isNull(ImageFile_.imageHash)
                .or()
                .in(ImageFile_.imageHash, new long[]{0, -1})
                .notEqual(ImageFile_.status, StatusContent.ONLINE.getCode());

        return query.build();
    }

    // Select all duplicate bookmarks that end with a "/"
    public Query<SiteBookmark> selectAllDuplicateBookmarks() {
        String[] urls = selectAllBooksmarkUrls();
        for (int i = 0; i < urls.length; i++) urls[i] = urls[i] + "/";

        QueryBuilder<SiteBookmark> query = store.boxFor(SiteBookmark.class).query();
        query.in(SiteBookmark_.url, urls, QueryBuilder.StringOrder.CASE_INSENSITIVE);

        return query.build();
    }
}

redpajama_set_name: RedPajamaGithub


text:
define(function (require) {

  'use strict';

  /**
   * Module dependencies
   */

  var defineComponent = require('flight/lib/component');

  /**
   * Module exports
   */

  return defineComponent(todoList);

  /**
   * Module function
   */

  function todoList() {

    var storage = { items: [] };

    var onNeedsTodoItems = function(){
      this.trigger('data-todo-items', storage);
    };

    var onNewTodoItem = function(event, data){
      var newItem = {description: data.description, completed: false};
      storage.items.push(newItem);
      this.trigger('data-todo-item-added', newItem);
    };

    this.after('initialize', function () {
      this.on('ui-needs-todo-items', onNeedsTodoItems);
      this.on('ui-new-todo-item', onNewTodoItem);
    });
  }

});


text:
This project is a boilerplate for Web application which uses Material UI components for building UI.
In this project you will find many variants of components from Matarial UI, also many of them are adopted to use in compositions with each other.
To read more details about Material UI library you can here: http://material-ui.com

Flexbox Gird was taken as grid system. Read more about Flexbox Grid: http://flexboxgrid.com

This project includes a starter file structure for Redux, prepared Webpack configuration for building, and simple ExpressJS server.

### Technologies and libraries
The following libs and technlogies were used:
* ```react``` (0.14)
* ```react-router```
* ```redux``` (```redux-thunk```, ```react-redux```)
* ```material-ui```
* ```Flexbox Grid``` (CSS)
* ```express```
* ```webpack```

```react-router``` is used as routing mechanizm between pages in Structor.
Equal router is used in final application after exporting pages.
This will be discussed below in chapter "Export pages".

```redux``` is a great implementation of Flux architechture and helps dramatically to build advanced Web apps.

```express``` used as back-end HTTP server for Web application.

### Structure of the source code
Below is a treeview of the structure where only that files shown which are described further.
In the description of each file or directory you may find information about how it is used by the builder and how you can change it for your purpose.
```
.structor/
 |--defaults/
 |   |--...
 |--desk/
 |   |--assets/
 |   |--index.html
 |   |--model.json
 |
 |--docs/
 |   |--...
 |--generators/
 |   |--react-es6
 |   |   |--scripts/
 |   |   |--generator.json
 |   |--...
 |
 |--src/
 |   |--default.js
 |   |--index.js
 |   |--...
 |
 |--templates/
 |   |--routes.js.tpl
 |   |--{pagename}.js.tpl
 |
 |--config.json
 |--proxy.json
build-conf/
 |--webpack.config.js
 |--webpack-dev.config.js
public/
 |--images/
 |   |--...
 |--resources/
 |   |--...
 |--index.html
src/
 |--...
 |--store/
 |   |--storeManager.js
 |   |--...
 |--main.js
server.js
```
#### Metainfo files
* ```.structor/``` - folder of project's metainfo for Structor.
* ```.structor/defaults/``` - a folder where data models variants of component are stored
* ```.structor/desk/index.html``` - html file of page in the Structor workspace.
Here you can add external resources which can not be included through the webpack require() mechanizm.
For example, jQuery library of modernizr.
Though, don't edit any existing links and refs - they are essential for edited page's workspace look and feel.
* ```.structor/desk/model.json``` - JSON data model of entire project.
* ```.structor/docs/``` - folder where Structor stores documentation for each component. There are markdown files named each after the component name.
* ```.structor/generators/``` - folder containing scripts and settings files for source code generators. Please refer to Structor docs chapter about source code generation.
* ```.structor/generators/react-es6/``` - all generators distributed through separated folders, this is one of the generators folder.
If you want to create the source code generator for React component of your own, you can clone this folder and modify scripts and configuration of generator.
But ofcourse, you have to know what data is passed to generator during React component generation, that information you may find in Structor documentation.
* ```.structor/generators/react-es6/scripts/``` - this is folder where generator javascripts files are. You can modify any file according to the rules of generation.
In this project all generators are using lodash templates.
* ```.structor/generators/react-es6/generator.json``` - do not modify the name of this file inside of generator's folder.
This is a configuration of generator, where paths of scripts, output files, and rules of naming are described.
* ```.structor/src/``` - files for Structor workspace.
* ```.structor/src/index.js``` - index file of React components which are used in current project.
Here you can add the references of third-party components which you want to see in Structor's workspace.
Also you can add here dependent resources like css, images, etc.
* ```.structor/src/default.js``` - main entry file for Structor's webpack, includes React Router config and connection with Redux storage in current project.
Find line: ```const store = storeManager();``` If your Redux storage is created in another way or locates in other place you have to edit this here.
* ```.structor/templates/``` - folder with page and router templates which are using for exporting workspace pages into real application.
* ```.structor/templates/routes.js.tpl``` - lodash template for React Router which will contain all routes of developed pages in Structor.
Currently Structor works with not nested routes.
* ```.structor/templates/{pagename}.js.tpl``` - lodash template for page's React component which will contain all components in particular page.
* ```.structor/config.json``` - configuration file. Currently has only relative path of the output folder for exported pages' source code.
* ```.structor/proxy.json``` - proxy configuration. Proxy configuration is used for setting up proxy in current ptoject while you are working with pages in Structor workspace.
So, components can make requests to backend server omiting CORS restriction.

#### Build scripts
* ```build-conf/webpack.config.js``` - this is webpack's config file for production.
The output of this config will be the minimized files in ```public/``` folder.
* ```build-conf/webpack-dev.config.js``` - this is webpack's config file for development stage of the project.

#### Output Web app files
* ```public/``` - folder where static compiled files are.
* ```public/index.html``` - index file of our Web app. If you have included manually resources into meta file ```.structor/desk/index.html``` don't forget to include them here as well.
* ```public/resources/``` - due to rewriting rules in express.js we need to separate resources from index file.

#### Source code
* ```src/``` - folder in which the source code of application is.
Folders are created using examples from Redux documentation.
* ```src/store/storeManager.js``` - file which is used as connection point of Redux store for Structor and for real app.
* ```src/main.js``` - entry point of Web app.
If you add manually React components into metainfo index.js you have to add them here as well.

```server.js``` - express backend server for current Web app.

### Starting Structor
If you download package from StructorMarket manually please execute the following commands in the project's directory:
* Run command: ```npm install```

Now you can start Structor from command line. To recognise the path of current project's folder Structor has
to be started in the root directory of current project or started with command argument ```-d``` pointing to project's folder.
* in project root dir: ```structor```
* in any other dir: ```structor -d <path to project directory>```

Once you have started Structor you will see the project's workspace. Now you can try to combine components with each other,
or generate new React components from the combination, or add new pages, or whatever you want.

This project has only three sample pages: ```/set1```, ```/set2```, and ```/progress```

### Exporting pages with routes
In any moment of work with Structor you can export existing pages into real pages with routes.
This can be done by selecting option "Export project" from main menu in Structor's workspace.

After that you will get a list of generated files of pages' components. And one more additional file for router configuration.
In current project these files will be generated into ```src/routes/``` folder. But, you can change this path in ```.structor/config.json``` file.

Also you may edit templates for output React components in ```.structor/templates/``` or add your own new with ".tpl" extension.

### Building Web app
* open main menu in Structor's workspace and select option ```Export project```
* Run command: ```npm run build```
* Run command: ```node ./server.js```
* Go to ```http://localhost:3000```


text:
package com.github.terma.gigaspacewebconsole.provider;

import com.gigaspaces.document.DocumentProperties;
import com.gigaspaces.entry.VirtualEntry;
import com.google.gson.*;

import java.lang.reflect.Type;
import java.util.Map;

public class EmbeddedObjectsConverter {

    private static final JsonSerializer<VirtualEntry> VIRTUAL_ENTRY_SERIALIZER =
            new JsonSerializer<VirtualEntry>() {

                @Override
                public JsonElement serialize(
                        final VirtualEntry virtualEntry, final Type type,
                        final JsonSerializationContext jsonSerializationContext) {
                    JsonObject jsonObject = new JsonObject();
                    jsonObject.addProperty("typeName", virtualEntry.getTypeName());
                    jsonObject.add("properties", jsonSerializationContext.serialize(virtualEntry.getProperties()));
                    return jsonObject;
                }

            };

    private static final Gson GSON = new GsonBuilder()
            .registerTypeHierarchyAdapter(VirtualEntry.class, VIRTUAL_ENTRY_SERIALIZER)
            .disableHtmlEscaping()
            .setPrettyPrinting()
            .create();

    public static String convert(final Object o) {
        if (o instanceof VirtualEntry
                || o instanceof DocumentProperties
                || o instanceof Map
                || o instanceof Iterable)
            return GSON.toJson(o);
        else return null;
    }

}


text:
package com.github.sormuras.listing.type;

import com.github.sormuras.listing.Annotatable;
import com.github.sormuras.listing.Annotation;
import com.github.sormuras.listing.Name;
import java.lang.annotation.ElementType;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.lang.model.AnnotatedConstruct;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.SimpleAnnotationValueVisitor8;
import javax.lang.model.util.SimpleTypeVisitor8;

/** Common {@link JavaType} factory collection parsing {@link javax.lang.model.type.TypeMirror}s. */
public interface JavaMirrors {

  /** Annotation value visitor adding members to the {@link Annotation} instance. */
  class AnnotationVisitor extends SimpleAnnotationValueVisitor8<Annotation, String> {
    private final Annotation annotation;

    AnnotationVisitor(Annotation annotation) {
      super(annotation);
      this.annotation = annotation;
    }

    @Override
    protected Annotation defaultAction(Object object, String name) {
      annotation.addObject(name, object);
      return annotation;
    }

    @Override
    public Annotation visitAnnotation(AnnotationMirror mirror, String name) {
      annotation.addMember(name, of(mirror));
      return annotation;
    }

    @Override
    public Annotation visitArray(List<? extends AnnotationValue> values, String name) {
      for (AnnotationValue value : values) {
        value.accept(this, name);
      }
      return annotation;
    }

    @Override
    public Annotation visitEnumConstant(VariableElement element, String name) {
      ClassType classType = (ClassType) JavaMirrors.of(element.asType());
      String constantName = element.getSimpleName().toString();
      List<String> simpleNames = new ArrayList<String>();
      classType.getNames().forEach(sn -> simpleNames.add(sn.getName()));
      simpleNames.add(constantName);
      Name constant = new Name(classType.getPackageName(), simpleNames);
      constant.setTarget(ElementType.FIELD);
      constant.setModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL);
      annotation.addMember(name, constant);
      return annotation;
    }

    @Override
    public Annotation visitType(TypeMirror type, String name) {
      annotation.addMember(name, l -> l.add(JavaMirrors.of(type)).add(".class"));
      return annotation;
    }
  }

  class Visitor extends SimpleTypeVisitor8<JavaType, Object> {

    @Override
    public JavaType visitArray(javax.lang.model.type.ArrayType type, Object tag) {
      return of(type);
    }

    @Override
    public JavaType visitDeclared(javax.lang.model.type.DeclaredType type, Object tag) {
      return of(type);
    }

    @Override
    public JavaType visitError(javax.lang.model.type.ErrorType type, Object tag) {
      return of(type);
    }

    @Override
    public JavaType visitNoType(javax.lang.model.type.NoType type, Object tag) {
      return of(type);
    }

    @Override
    public JavaType visitPrimitive(javax.lang.model.type.PrimitiveType type, Object tag) {
      return of(type);
    }

    @Override
    public JavaType visitTypeVariable(javax.lang.model.type.TypeVariable type, Object tag) {
      return of(type);
    }

    @Override
    public JavaType visitWildcard(javax.lang.model.type.WildcardType type, Object tag) {
      return of(type);
    }
  }

  static <A extends Annotatable> A annotate(A target, AnnotatedConstruct source) {
    source.getAnnotationMirrors().forEach(m -> target.addAnnotation(of(m)));
    return target;
  }

  /** Create {@link Annotation} based on {@link AnnotationMirror} instance. */
  static Annotation of(AnnotationMirror mirror) {
    Element element = mirror.getAnnotationType().asElement();
    Annotation annotation = new Annotation(Name.of(element));
    Map<? extends ExecutableElement, ? extends AnnotationValue> values = mirror.getElementValues();
    if (values.isEmpty()) {
      return annotation;
    }
    AnnotationVisitor visitor = new AnnotationVisitor(annotation);
    for (ExecutableElement executableElement : values.keySet()) {
      String name = executableElement.getSimpleName().toString();
      AnnotationValue value = values.get(executableElement);
      value.accept(visitor, name);
    }
    return annotation;
  }

  /** Create {@link ArrayType} based on {@link javax.lang.model.type.ArrayType} instance. */
  static ArrayType of(javax.lang.model.type.ArrayType type) {
    ArrayType arrayType = new ArrayType();
    TypeMirror mirror = type;
    while (mirror instanceof javax.lang.model.type.ArrayType) {
      ArrayDimension dimension = annotate(new ArrayDimension(), mirror);
      arrayType.getDimensions().add(dimension);
      mirror = ((javax.lang.model.type.ArrayType) mirror).getComponentType();
    }
    arrayType.setComponentType(of(mirror));
    return arrayType;
  }

  /** Create {@link ClassType} based on {@link javax.lang.model.type.DeclaredType} instance. */
  static ClassType of(javax.lang.model.type.DeclaredType type) {
    ClassType classType = new ClassType();
    // extract package name
    Element packageElement = type.asElement();
    while (packageElement.getKind() != ElementKind.PACKAGE) {
      packageElement = packageElement.getEnclosingElement();
    }
    PackageElement casted = (PackageElement) packageElement;
    classType.setPackageName(casted.getQualifiedName().toString());
    // extract simple names and type arguments
    TypeMirror actualType = type;
    for (Element element = type.asElement();
        element.getKind().isClass() || element.getKind().isInterface();
        element = element.getEnclosingElement()) {
      ClassName name = new ClassName(); // annotate(new ClassName(), element); // element.asType());
      name.setName(element.getSimpleName().toString());
      classType.getNames().add(0, name);
      if ((actualType instanceof javax.lang.model.type.DeclaredType)) {
        javax.lang.model.type.DeclaredType dt = (javax.lang.model.type.DeclaredType) actualType;
        annotate(name, dt);
        for (TypeMirror ta : dt.getTypeArguments()) {
          AnnotatedConstruct annotatedConstruct = ta;
          if (ta instanceof javax.lang.model.type.DeclaredType) {
            annotatedConstruct = ((javax.lang.model.type.DeclaredType) ta).asElement();
          }
          JavaType javaType = annotate(JavaMirrors.of(ta), annotatedConstruct);
          name.getTypeArguments().add(TypeArgument.of(javaType));
        }
        actualType = dt.getEnclosingType();
      }
    }
    return classType;
  }

  /** Create {@link JavaType} based on {@link javax.lang.model.type.NoType} instance. */
  static JavaType of(javax.lang.model.type.NoType type) {
    if (type.getKind() == TypeKind.VOID) {
      return new VoidType();
    }
    throw new AssertionError("Unsupported no type: " + type.getKind());
  }

  /** Create {@link PrimitiveType} based on {@link javax.lang.model.type.PrimitiveType} instance. */
  static PrimitiveType of(javax.lang.model.type.PrimitiveType type) {
    TypeKind kind = type.getKind();
    if (kind == TypeKind.BOOLEAN) {
      return annotate(new PrimitiveType.BooleanType(), type);
    }
    if (kind == TypeKind.INT) {
      return annotate(new PrimitiveType.IntType(), type);
    }
    if (kind == TypeKind.LONG) {
      return annotate(new PrimitiveType.LongType(), type);
    }
    if (kind == TypeKind.CHAR) {
      return annotate(new PrimitiveType.CharType(), type);
    }
    if (kind == TypeKind.FLOAT) {
      return annotate(new PrimitiveType.FloatType(), type);
    }
    if (kind == TypeKind.DOUBLE) {
      return annotate(new PrimitiveType.DoubleType(), type);
    }
    if (kind == TypeKind.BYTE) {
      return annotate(new PrimitiveType.ByteType(), type);
    }
    if (kind == TypeKind.SHORT) {
      return annotate(new PrimitiveType.ShortType(), type);
    }
    throw new AssertionError("Unsupported primitive type: " + type.getKind());
  }

  static JavaType of(javax.lang.model.type.TypeMirror mirror) {
    Visitor visitor = new Visitor();
    JavaType result = mirror.accept(visitor, null);
    return result;
  }

  static JavaType of(javax.lang.model.type.TypeVariable type) {
    TypeVariable variable = new TypeVariable();
    variable.setName(type.asElement().getSimpleName().toString());
    // TODO bounds...
    return variable;
  }

  /** Create {@link WildcardType} based on {@link javax.lang.model.type.WildcardType} instance. */
  static WildcardType of(javax.lang.model.type.WildcardType mirror) {
    TypeMirror extendsBound = mirror.getExtendsBound();
    if (extendsBound != null) {
      return annotate(WildcardType.subtypeOf(of(extendsBound)), mirror);
    }
    TypeMirror superBound = mirror.getSuperBound();
    if (superBound != null) {
      return annotate(WildcardType.supertypeOf(of(superBound)), mirror);
    }
    return annotate(new WildcardType(), mirror);
  }
}


text:
#ifndef __FinishedPage__
#define __FinishedPage__

class FinishedPage : public CPropertyPage
{
  DECLARE_DYNCREATE(FinishedPage)

public:

  FinishedPage();

  ~FinishedPage();

  virtual BOOL OnSetActive();

protected:

  virtual void DoDataExchange(CDataExchange* pDX);

  virtual BOOL OnInitDialog();

  DECLARE_MESSAGE_MAP()

private:

  CString _text;
};

#endif // __FinishedPage__


text:
import os
from setuptools import setup

with open(os.path.join(os.path.dirname(__file__),
                       'README')) as f:
    readme = f.read().strip()

setup(
    name='python-geoip',
    author='Armin Ronacher',
    author_email='armin.ronacher@active-4.com',
    version='1.2',
    url='http://github.com/mitsuhiko/python-geoip',
    long_description=readme,
    description='Provides GeoIP functionality for Python.',
    py_modules=['geoip'],
    zip_safe=False,
    classifiers=[
        'Programming Language :: Python',
        'License :: OSI Approved :: BSD License',
    ],
)


text:
<?xml version="1.0" encoding="UTF-8"?>

<!-- Copyright (C) 2017 Marcelo Vinícius Cysneiros Aragão Permission is hereby
    granted, free of charge, to any person obtaining a copy of this software
    and associated documentation files (the "Software"), to deal in the Software
    without restriction, including without limitation the rights to use, copy,
    modify, merge, publish, distribute, sublicense, and/or sell copies of the
    Software, and to permit persons to whom the Software is furnished to do so,
    subject to the following conditions: The above copyright notice and this
    permission notice shall be included in all copies or substantial portions
    of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
    KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
    NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
    DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
    ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    DEALINGS IN THE SOFTWARE. -->

<Configuration status="WARN">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT"><PatternLayout pattern="%d %p %m%n" /></Console>
    </Appenders>
    <Loggers>
        <Root level="error"> <appender-ref ref="Console" /></Root>
    </Loggers>
</Configuration>