package ecologylab.bigsemantics.seeding; import java.util.ArrayList; import java.util.Iterator; import javax.swing.JOptionPane; import ecologylab.bigsemantics.collecting.Seeding; import ecologylab.bigsemantics.collecting.SemanticsGlobalScope; import ecologylab.bigsemantics.collecting.SemanticsSessionScope; import ecologylab.bigsemantics.namesandnums.SemanticsSessionObjectNames; import ecologylab.collections.Scope; import ecologylab.serialization.ElementStateOrmBase; import ecologylab.serialization.SimplTypesScope; import ecologylab.serialization.annotations.simpl_collection; import ecologylab.serialization.annotations.simpl_inherit; import ecologylab.serialization.annotations.simpl_nowrap; import ecologylab.serialization.annotations.simpl_scalar; import ecologylab.serialization.annotations.simpl_scope; /** * A collection of seeds that will be performed by the agent, or elsewhere, by composition space * services. These are directives from the user via the startup enviornment. * * @author andruid */ @simpl_inherit public class SeedSet<S extends Seed> extends ElementStateOrmBase implements SemanticsSessionObjectNames, Iterable<S> { static SimplTypesScope ts = BaseSeedTranslations.get(); @simpl_scalar protected boolean dontPlayOnStart; @simpl_scalar protected String id; @simpl_scalar protected String category; @simpl_scalar protected String description; @simpl_collection @simpl_scope(BaseSeedTranslations.TSCOPE_NAME) @simpl_nowrap protected ArrayList<S> seeds; /** * when this is set to true, some thread is now iterating this seed set's seeds field, thus you * can't modify it right now. instead you use pendingSeedsToAdd. */ private boolean duringIteration = false; /** * seeds in this set will be added to seeds in the next seeding phase (and also removed from * here). */ private ArrayList<S> pendingSeedsToAdd = new ArrayList<S>(); public SeedSet() { } // public static SeedSet getFromXML(String seedSetString) // { // try // { // return (SeedSet) translateFromXMLCharSequence(seedSetString, CFServicesTranslations.get()); // } catch (XMLTranslationException e) // { // println("ERROR parsing seedSetString."); // e.printStackTrace(); // return null; // } // } /** * Number of SearchState seeds specified in this. */ private int numSearches = 0; /** * Coordinate downloading of search results for seeding searches. */ SeedDistributor resultDistributer; /** * Some of seeds such as YahooBuzzType have a starting seed(a buzz page) from an initial SeedSet * and create a new SeedSet from the buzz page. Reference to the parent SeedSet from a child * SeedSet. */ SeedSet parentSeedSet; public SeedDistributor seedDistributer(SemanticsGlobalScope infoCollector) { SeedDistributor result = resultDistributer; if (result == null) { if (parentSeedSet != null) // recurse to get from parent { result = parentSeedSet.seedDistributer(infoCollector); // result.moreSearches(numSearches); } else result = new SeedDistributor(infoCollector); this.resultDistributer = result; } return result; } /** * In case for replace seed with the curated-seeds, SeedSet is reused. So, old resultDistributer * is used without getting reset. Maybe there is better place to reset ResultDistributer -- eunyee * * @return */ public void resetResultDistributer() { if (resultDistributer != null) resultDistributer.reset(); } /** * Set the reference of the parent SeedSet. * * @param seedDistributer */ public void setParentSeedSet(SeedSet seedSet) { this.parentSeedSet = seedSet; } /** * Update parameters for next result set. Set time stamp to now. Perform the resulting search by * using the engine as a key . * * @param semanticsSessionScope */ public void performNextSeeding(Scope scope) { // resultDistributer = null; resetResultDistributer(); performSeeding(scope, true); } public void performSeeding(Scope scope) { performSeeding(scope, false); } int startingResultNum; public int getStartingResultNum() { return startingResultNum; } /** * Bring the seeds into the agent or directly into the compostion. * * @param scope * Context passed between services calls. */ public void performSeeding(Scope scope, boolean nextSearch) { if (pendingSeedsToAdd.size() > 0) { // be careful of possible deadlock here synchronized (pendingSeedsToAdd) { synchronized (seeds) { for (S seed : pendingSeedsToAdd) seeds.add(seed); } pendingSeedsToAdd.clear(); } } if (size() == 0) return; SemanticsSessionScope infoCollector = (SemanticsSessionScope) scope.get(INFO_COLLECTOR); Seeding seeding = infoCollector.getSeeding(); seeding.trackFirstSeedSet(this); seeding.setPlayOnStart(true); seeding.beginSeeding(); numSearches = 0; // reset each time performSeeding is called // search bookkeeping for each seed duringIteration = true; for (Seed seed : seeds) { if (nextSearch) { // seed.seedDistributer = null; // it seems that we can reuse the previous seed distributor int thisStartingResultNum = seed.nextResultSet(); if (thisStartingResultNum > startingResultNum) startingResultNum = thisStartingResultNum; } else if (seed.initializeSeedingSteps(this, numSearches)) numSearches++; } duringIteration = false; // We need the same two for loops (above and below), because // it requires to have total searchNum before it starts performSeeding. // Do not try to aggregate these two for loops unless you have better structure. boolean aSeedingIsPerformed = false; boolean shouldEndSeeding = true; duringIteration = true; for (Seed seed : seeds) { seed.fixNumResults(); if (seed.isActive()) // false if inactive { debug("perform seeding: " + seed); if (shouldNotEndSeeding(seed)) shouldEndSeeding = false; seed.performSeedingSteps(infoCollector); aSeedingIsPerformed = true; SeedPeer seedPeer = seed.getSeedPeer(); if (seedPeer != null) seedPeer.notifyInterface(scope, SEARCH_DASH_BOARD); seed.setActive(false); // does not affect SearchState or Feed } } duringIteration = false; if (aSeedingIsPerformed && shouldEndSeeding) { seeding.endSeeding(); seedDistributer(infoCollector).stop(); } } /** * * @param seed * @return true if we should not call endSeeding() immediately because this seed has outsourced * its seeding steps (e.g. through a SeedDistributor). otherwise false. */ private boolean shouldNotEndSeeding(Seed seed) { if (seed instanceof SearchState) return true; if (seed instanceof Feed) return true; return false; } public String toString() { return "SeedSet[" + size() + "]"; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getCategory() { return category; } public void setCategory(String category) { this.category = category; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } private int handleMoreSeedsDialogue(SemanticsSessionScope infoCollector) { String seedText = ""; for (int i = 0; i < size(); i++) seedText += " " + ((Seed) get(i)).valueString() + "\n"; if (!"".equals(seedText)) seedText += "\n"; String optionsMessage = "combinFormation has received a new set of seeds:\n" + seedText + "\t- Do you want to mix these new seeds with the already existing seeds?\n" + "\t- Do you want to replace the current seeds with these new seeds?\n" + "\t- Do you want to ignore these new seeds?"; return infoCollector.showOptionsDialog(optionsMessage, "Warning", SeedCf.MULTIPLE_REQUESTS_DIALOG_OPTIONS, 0); } public void handleMoreSeeds(Scope clientConnectionScope, int selectedOption) { if (selectedOption == SeedCf.MULTIPLE_REQUESTS_ASK_USER) // if (true) { if (size() == 0) { selectedOption = SeedCf.MULTIPLE_REQUESTS_IGNORE; debug("Received seeding request with 0 seeds. Ignoring!"); } else { SemanticsSessionScope infoCollector = (SemanticsSessionScope) clientConnectionScope.get(INFO_COLLECTOR); selectedOption = handleMoreSeedsDialogue(infoCollector); } } switch (selectedOption) { case SeedCf.MULTIPLE_REQUESTS_MIX: performSeeding(clientConnectionScope); break; case SeedCf.MULTIPLE_REQUESTS_REPLACE: debug("handleMoreSeeds(REPLACE) " + clientConnectionScope.dump()); SemanticsSessionScope infoCollector = (SemanticsSessionScope) clientConnectionScope.get(INFO_COLLECTOR); infoCollector.clear(); performSeeding(clientConnectionScope); break; case SeedCf.MULTIPLE_REQUESTS_IGNORE: case JOptionPane.CLOSED_OPTION: debug("Ignoring multiple request."); break; default: } } public void add(S seed, SemanticsGlobalScope infoCollector) { if (seed != null) if (seeds == null) seeds = new ArrayList<S>(); if (duringIteration) { synchronized(pendingSeedsToAdd) { pendingSeedsToAdd.add(seed); } } else { synchronized(seeds) { seeds.add(seed); } } seed.setSeedSet(this); Seeding seeding = infoCollector.getSeeding(); // Test for hetero/homogeneity Iterator iterator = iterator(); while (iterator.hasNext()) { Seed s = (Seed) iterator.next(); if (size() == 1) { seeding.setHeterogeneousSearchScenario(!s.isHomogenousSeed()); break; } else { // More than one seed. // Even if one isn't homogeneous, scenario is hetero if (!s.isHomogenousSeed()) { seeding.setHeterogeneousSearchScenario(true); break; } } } } public void clear() { if (seeds != null) seeds.clear(); } public int size() { return (seeds != null) ? seeds.size() : 0; } @Override public Iterator<S> iterator() { return seeds.iterator(); } public S get(int i) { return seeds != null ? (i >= 0 && i < seeds.size() ? seeds.get(i) : null) : null; } public int indexOf(S that) { return seeds != null ? seeds.indexOf(that) : -1; } public boolean isEmpty() { return seeds == null || seeds.isEmpty(); } }