diff options
author | Jon Bratseth <bratseth@yahoo-inc.com> | 2016-06-15 23:09:44 +0200 |
---|---|---|
committer | Jon Bratseth <bratseth@yahoo-inc.com> | 2016-06-15 23:09:44 +0200 |
commit | 72231250ed81e10d66bfe70701e64fa5fe50f712 (patch) | |
tree | 2728bba1131a6f6e5bdf95afec7d7ff9358dac50 /container-search/src/main/java/com/yahoo/search/pagetemplates/model |
Publish
Diffstat (limited to 'container-search/src/main/java/com/yahoo/search/pagetemplates/model')
11 files changed, 779 insertions, 0 deletions
diff --git a/container-search/src/main/java/com/yahoo/search/pagetemplates/model/AbstractChoice.java b/container-search/src/main/java/com/yahoo/search/pagetemplates/model/AbstractChoice.java new file mode 100644 index 00000000000..069598b2e02 --- /dev/null +++ b/container-search/src/main/java/com/yahoo/search/pagetemplates/model/AbstractChoice.java @@ -0,0 +1,31 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.search.pagetemplates.model; + +import com.yahoo.component.provider.FreezableClass; + +/** + * Abstract superclass of various kinds of choices. + * + * @author <a href="mailto:bratseth@yahoo-inc.com">Jon Bratseth</a> + */ +public abstract class AbstractChoice extends FreezableClass implements PageElement { + + private String method; + + /** + * Returns the choice method to use - a string interpreted by the resolver in use, + * or null to use any available method + */ + public String getMethod() { return method; } + + public void setMethod(String method) { + ensureNotFrozen(); + this.method=method; + } + + // TODO: is this really choices between classes in general, or e.g. subclasses of Section? + /** Returns true if this choice is (partially or completely) a choice between the given type */ + @SuppressWarnings("rawtypes") + public abstract boolean isChoiceBetween(Class pageTemplateModelClass); + +} diff --git a/container-search/src/main/java/com/yahoo/search/pagetemplates/model/Choice.java b/container-search/src/main/java/com/yahoo/search/pagetemplates/model/Choice.java new file mode 100644 index 00000000000..a1932012236 --- /dev/null +++ b/container-search/src/main/java/com/yahoo/search/pagetemplates/model/Choice.java @@ -0,0 +1,114 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.search.pagetemplates.model; + +import java.util.*; + +/** + * A choice between some alternative lists of page elements. + * + * @author <a href="mailto:bratseth@yahoo-inc.com">Jon Bratseth</a> + */ +public final class Choice extends AbstractChoice { + + private List<List<PageElement>> alternatives=new ArrayList<>(3); + + /** Creates an empty choice */ + public Choice() { } + + /** Creates a choice having a single alternative having a single page element */ + public static Choice createSingleton(PageElement singletonAlternative) { + Choice choice=new Choice(); + choice.alternatives().add(createSingletonList(singletonAlternative)); + return choice; + } + + /** Creates a choice in which each alternative consists of a single element */ + public static Choice createSingletons(List<PageElement> alternatives) { + Choice choice=new Choice(); + for (PageElement alternative : alternatives) + choice.alternatives().add(createSingletonList(alternative)); + return choice; + } + + private static List<PageElement> createSingletonList(PageElement member) { + List<PageElement> list=new ArrayList<>(); + list.add(member); + return list; + } + + /** + * Creates a choice between some alternatives. This method takes a copy of the given lists. + */ + public Choice(List<List<PageElement>> alternatives) { + for (List<PageElement> alternative : alternatives) + this.alternatives.add(new ArrayList<>(alternative)); + } + + /** + * Returns the alternatives of this as a live reference to the alternatives of this. + * The list and elements may be modified unless this is frozen. This is never null. + */ + public List<List<PageElement>> alternatives() { return alternatives; } + + /** Convenience shorthand of <code>return alternatives().get(index)</code> */ + public List<PageElement> get(int index) { + return alternatives.get(index); + } + + /** Convenience shorthand for <code>if (alternative!=null) alternatives().add(alternative)</code> */ + public void add(List<PageElement> alternative) { + if (alternative!=null) + alternatives.add(new ArrayList<>(alternative)); + } + + /** Returns true only if there are no alternatives in this */ + public boolean isEmpty() { return alternatives.size()==0; } + + /** Answers true if this is either a choice between the given class, or between Lists of the given class */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Override + public boolean isChoiceBetween(Class pageTemplateModelElementClass) { + List firstNonEmpty=null; + for (List<PageElement> value : alternatives) { + if (pageTemplateModelElementClass.isAssignableFrom(value.getClass())) return true; + if (value instanceof List) { + List listValue=(List)value; + if (listValue.size()>0) + firstNonEmpty=listValue; + } + } + if (firstNonEmpty==null) return false; + return (pageTemplateModelElementClass.isAssignableFrom(firstNonEmpty.get(0).getClass())); + } + + @Override + public void freeze() { + if (isFrozen()) return; + super.freeze(); + for (ListIterator<List<PageElement>> i=alternatives.listIterator(); i.hasNext(); ) { + List<PageElement> alternative=i.next(); + for (PageElement alternativeElement : alternative) + alternativeElement.freeze(); + i.set(Collections.unmodifiableList(alternative)); + } + alternatives= Collections.unmodifiableList(alternatives); + } + + /** Accepts a visitor to this structure */ + @Override + public void accept(PageTemplateVisitor visitor) { + visitor.visit(this); + for (List<PageElement> alternative : alternatives) { + for (PageElement alternativeElement : alternative) + alternativeElement.accept(visitor); + } + } + + @Override + public String toString() { + if (alternatives.isEmpty()) return "(empty choice)"; + if (alternatives.size()==1) return alternatives.get(0).toString(); + return "a choice between " + alternatives; + } + +} diff --git a/container-search/src/main/java/com/yahoo/search/pagetemplates/model/Layout.java b/container-search/src/main/java/com/yahoo/search/pagetemplates/model/Layout.java new file mode 100644 index 00000000000..f8e00b78787 --- /dev/null +++ b/container-search/src/main/java/com/yahoo/search/pagetemplates/model/Layout.java @@ -0,0 +1,50 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.search.pagetemplates.model; + +/** + * The layout of a section + * + * @author <a href="mailto:bratseth@yahoo-inc.com">Jon Bratseth</a> + */ +// This is not made an enum, to allow the value set to be extendible. +// It is not explicitly made immutable +// to enable adding of internal state later (esp. parameters). +// If this becomes mutable, the creation scheme must be changed +// such that each fromString returns a unique instance, and +// the name must become a (immutable) type. +public class Layout { + + /** The built in "column" layout */ + public static final Layout column=new Layout("column"); + /** The built in "row" layout */ + public static final Layout row=new Layout("row"); + + private String name; + + public Layout(String name) { + this.name=name; + } + + public String getName() { return name; } + + public @Override int hashCode() { return name.hashCode(); } + + public @Override boolean equals(Object o) { + if (o==this) return true; + if (! (o instanceof Layout)) return false; + Layout other=(Layout)o; + return this.name.equals(other.name); + } + + /** Returns a layout having this string as name, or null if the given string is null or empty */ + public static Layout fromString(String layout) { + //if (layout==null) return null; + //if (layout) + if (layout.equals("column")) return column; + if (layout.equals("row")) return row; + return new Layout(layout); + } + + public @Override String toString() { return "layout '" + name + "'"; } + +} diff --git a/container-search/src/main/java/com/yahoo/search/pagetemplates/model/MapChoice.java b/container-search/src/main/java/com/yahoo/search/pagetemplates/model/MapChoice.java new file mode 100644 index 00000000000..33c3bba9a77 --- /dev/null +++ b/container-search/src/main/java/com/yahoo/search/pagetemplates/model/MapChoice.java @@ -0,0 +1,69 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.search.pagetemplates.model; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * A choice between different possible mapping functions of a set of values to a set of placeholder ids. + * A <i>resolution</i> of this choice consists of choosing a unique value for each placeholder id + * (hence a map choice is valid iff there are at least as many values as placeholder ids). + * <p> + * Each unique set of mappings (pairs) from values to placeholder ids is a separate possible + * alternative of this choice. The alternatives are not listed explicitly but are generated as needed. + * + * @author <a href="mailto:bratseth@yahoo-inc.com">Jon Bratseth</a> + */ +public class MapChoice extends AbstractChoice { + + private List<String> placeholderIds=new ArrayList<>(); + + private List<List<PageElement>> values=new ArrayList<>(); + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Override + public boolean isChoiceBetween(Class pageTemplateModelElementClass) { + List<PageElement> firstNonEmpty=null; + for (List<PageElement> value : values) + if (value.size()>0) + firstNonEmpty=value; + if (firstNonEmpty==null) return false; + return (pageTemplateModelElementClass.isAssignableFrom(firstNonEmpty.get(0).getClass())); + } + + /** + * Returns the placeholder ids (the "to" of the mapping) of this as a live reference which can be modified unless + * this is frozen. + */ + public List<String> placeholderIds() { return placeholderIds; } + + /** + * Returns the values (the "from" of the mapping) of this as a live reference which can be modified unless + * this is frozen. Note that each single choice of values within this is also a list of values. This is + * the inner list. + */ + public List<List<PageElement>> values() { return values; } + + @Override + public void freeze() { + if (isFrozen()) return; + super.freeze(); + placeholderIds=Collections.unmodifiableList(placeholderIds); + values=Collections.unmodifiableList(values); + } + + /** Accepts a visitor to this structure */ + public @Override void accept(PageTemplateVisitor visitor) { + visitor.visit(this); + for (List<PageElement> valueEntry : values) + for (PageElement value : valueEntry) + value.accept(visitor); + } + + @Override + public String toString() { + return "mapping to placeholders " + placeholderIds; + } + +} diff --git a/container-search/src/main/java/com/yahoo/search/pagetemplates/model/PageElement.java b/container-search/src/main/java/com/yahoo/search/pagetemplates/model/PageElement.java new file mode 100644 index 00000000000..fba58f069ec --- /dev/null +++ b/container-search/src/main/java/com/yahoo/search/pagetemplates/model/PageElement.java @@ -0,0 +1,16 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.search.pagetemplates.model; + +import com.yahoo.component.provider.Freezable; + +/** + * Implemented by all page template model classes + * + * @author <a href="mailto:bratseth@yahoo-inc.com">Jon Bratseth</a> + */ +public interface PageElement extends Freezable { + + /** Accepts a visitor to this structure */ + public void accept(PageTemplateVisitor visitor); + +} diff --git a/container-search/src/main/java/com/yahoo/search/pagetemplates/model/PageTemplateVisitor.java b/container-search/src/main/java/com/yahoo/search/pagetemplates/model/PageTemplateVisitor.java new file mode 100644 index 00000000000..d7ebd3d1169 --- /dev/null +++ b/container-search/src/main/java/com/yahoo/search/pagetemplates/model/PageTemplateVisitor.java @@ -0,0 +1,41 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.search.pagetemplates.model; + +import com.yahoo.search.pagetemplates.PageTemplate; + +/** + * Superclass of visitors over the page template object structure + * + * @author <a href="mailto:bratseth@yahoo-inc.com">Jon Bratseth</a> + */ +public class PageTemplateVisitor { + + /** Called each time a page template is encountered. This default implementation does nothing */ + public void visit(PageTemplate pageTemplate) { + } + + /** Called each time a source or source placeholder is encountered. This default implementation does nothing */ + public void visit(Source source) { + } + + /** Called each time a section or section placeholder is encountered. This default implementation does nothing */ + public void visit(Section section) { + } + + /** Called each time a renderer is encountered. This default implementation does nothing */ + public void visit(Renderer renderer) { + } + + /** Called each time a choice is encountered. This default implementation does nothing */ + public void visit(Choice choice) { + } + + /** Called each time a map choice is encountered. This default implementation does nothing */ + public void visit(MapChoice choice) { + } + + /** Called each time a placeholder is encountered. This default implementation does nothing */ + public void visit(Placeholder placeholder) { + } + +} diff --git a/container-search/src/main/java/com/yahoo/search/pagetemplates/model/Placeholder.java b/container-search/src/main/java/com/yahoo/search/pagetemplates/model/Placeholder.java new file mode 100644 index 00000000000..cf7a85fc779 --- /dev/null +++ b/container-search/src/main/java/com/yahoo/search/pagetemplates/model/Placeholder.java @@ -0,0 +1,49 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.search.pagetemplates.model; + +/** + * A source placeholder is replaced with a list of source instances at evaluation time. + * Source placeholders may not have any content themselves - attempting to call any setter on this + * results in a IllegalStateException. + * + * @author <a href="mailto:bratseth@yahoo-inc.com">Jon Bratseth</a> + */ +public class Placeholder implements PageElement { + + private String id; + + private MapChoice valueContainer=null; + + /** Creates a source placeholder with an id. */ + public Placeholder(String id) { + this.id=id; + } + + public String getId() { return id; } + + /** Returns the element which contains the value(s) of this placeholder. Never null. */ + public MapChoice getValueContainer() { return valueContainer; } + + public void setValueContainer(MapChoice valueContainer) { this.valueContainer=valueContainer; } + + public @Override void freeze() {} + + /** Accepts a visitor to this structure */ + public @Override void accept(PageTemplateVisitor visitor) { + visitor.visit(this); + } + + public @Override String toString() { + return "source placeholder '" + id + "'"; + } + + /** + * This method always returns false, is a Placeholder always is mutable. + * (freeze() is a NOOP.) + */ + @Override + public boolean isFrozen() { + return false; + } + +} diff --git a/container-search/src/main/java/com/yahoo/search/pagetemplates/model/Renderer.java b/container-search/src/main/java/com/yahoo/search/pagetemplates/model/Renderer.java new file mode 100644 index 00000000000..4564ceeef3c --- /dev/null +++ b/container-search/src/main/java/com/yahoo/search/pagetemplates/model/Renderer.java @@ -0,0 +1,88 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.search.pagetemplates.model; + +import com.yahoo.component.provider.FreezableClass; +import com.yahoo.protect.Validator; + +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * A description of a way to present data items from a source. + * All data items has a default renderer. This can be overridden or parametrized by + * an explicit renderer. + * + * @author <a href="mailto:bratseth@yahoo-inc.com">Jon Bratseth</a> + */ +public final class Renderer extends FreezableClass implements PageElement { + + private String name; + + private String rendererFor; + + private Map<String,String> parameters =new LinkedHashMap<>(); + + public Renderer(String name) { + setName(name); + } + + /** + * Returns the name of this renderer (never null). + * The name should be recognized by the system receiving results for rendering + */ + public String getName() { return name; } + + public final void setName(String name) { + ensureNotFrozen(); + Validator.ensureNotNull("renderer name",name); + this.name=name; + } + + /** + * Returns the name of the kind of data this is a renderer for. + * This is used to allow frontends to dispatch the right data items (hits) to + * the right renderer in the case where the data consists of a heterogeneous list. + * <p> + * This is null if this is a renderer for a whole section, or if this is a renderer + * for all kinds of data from a particular source <i>and</i> this is not frozen. + * <p> + * Otherwise, it is either the name of the source this is the renderer for, + * <i>or</i> the renderer for all data items having this name as a <i>type</i>. + * <p> + * This, a (frontend) dispatcher of data to renderers should for each data item: + * <ul> + * <li>use the renderer having the same name as any <code>type</code> name set of the data item + * <li>if no such renderer, use the renderer having <code>rendererFor</code> equal to the data items <code>source</code> + * <li>if no such renderer, use a default renderer + * </ul> + */ + public String getRendererFor() { return rendererFor; } + + public void setRendererFor(String rendererFor) { + ensureNotFrozen(); + this.rendererFor=rendererFor; + } + + /** + * Returns the parameters of this renderer as a live reference (never null). + * The parameters will be passed to the renderer with each result + */ + public Map<String,String> parameters() { return parameters; } + + public @Override void freeze() { + if (isFrozen()) return; + super.freeze(); + parameters = Collections.unmodifiableMap(parameters); + } + + /** Accepts a visitor to this structure */ + public @Override void accept(PageTemplateVisitor visitor) { + visitor.visit(this); + } + public @Override String toString() { + return "renderer '" + name + "'"; + } + +} diff --git a/container-search/src/main/java/com/yahoo/search/pagetemplates/model/Section.java b/container-search/src/main/java/com/yahoo/search/pagetemplates/model/Section.java new file mode 100644 index 00000000000..0a980419853 --- /dev/null +++ b/container-search/src/main/java/com/yahoo/search/pagetemplates/model/Section.java @@ -0,0 +1,177 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.search.pagetemplates.model; + +import com.yahoo.component.provider.FreezableClass; +import com.yahoo.search.query.Sorting; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * An element of a page template corresponding to a physical area of the layout of the final physical page. + * Pages are freezable - once frozen calling a setter will cause an IllegalStateException, and returned + * live collection references are unmodifiable + * + * @author <a href="mailto:bratseth@yahoo-inc.com">Jon Bratseth</a> + */ +public class Section extends FreezableClass implements PageElement { + + private final String id; + + private Layout layout=Layout.column; + + private String region; + + /** The elements of this - sources, subsections etc. and/or choices of the same */ + private List<PageElement> elements=new ArrayList<>(); + + /** Filtered versions of elements pre-calculated at freeze time */ + private List<PageElement> sections, sources, renderers; + + private int max=-1; + + private int min=-1; + + private Sorting order=null; + + private static AtomicInteger nextId=new AtomicInteger(); + + public Section() { + this(null); + } + + /** Creates a section with an id (or null if no id) */ + public Section(String id) { + if (id==null || id.isEmpty()) + this.id=String.valueOf("section_" + nextId.incrementAndGet()); + else + this.id=id; + } + + /** Returns a unique id of this section within the page. Used for referencing and identification. Never null. */ + public String getId() { return id; } + + /** + * Returns the layout identifier describing the kind of layout which should be used by the rendering engine to + * lay out the content of this section. This is never null. Default: "column". + */ + public Layout getLayout() { return layout; } + + /** Sets the layout. If the layout is set to null it will become Layout.column */ + public void setLayout(Layout layout) { + ensureNotFrozen(); + if (layout==null) layout=Layout.column; + this.layout=layout; + } + + /** + * Returns the identifier telling the layout of the containing section where this section should be placed. + * Permissible values, and whether this is mandatory is determined by the particular layout identifier of the parent. + * May be null if a placement is not required by the containing layout, or if this is the top-level section. + * This is null by default. + */ + public String getRegion() { return region; } + + public void setRegion(String region) { + ensureNotFrozen(); + this.region=region; + } + + /** + * Returns the elements of this - sources, subsections and presentations and/or choices of these, + * as a live reference which can be modified to change the content of this (unless this is frozen). + * <p> + * All elements are kept in a single list to allow multiple elements of each type to be nested within separate + * choices, and to maintain the internal order of elements of various types, which is sometimes significant. + * To extract a certain kind of elements (say, sources), the element list must be traversed to collect + * all source elements as well as all choices of sources. + * <p> + * This list is never null but may be empty. + */ + public List<PageElement> elements() { return elements; } + + /** + * Convenience method which returns the elements <b>and choices</b> of the given type in elements as a + * read-only list. Not that as this returns both concrete elements and choices betwen them, + * the list element cannot be case to the given class - this must be used in conjunction + * with a resolve which contains the resolution to the choices. + * + * @param pageTemplateModelElementClass type to returns elements and choices of, a subtype of PageElement + */ + public List<PageElement> elements(@SuppressWarnings("rawtypes") Class pageTemplateModelElementClass) { + if (isFrozen()) { // Use precalculated lists + if (pageTemplateModelElementClass==Section.class) + return sections; + else if (pageTemplateModelElementClass==Source.class) + return sources; + else if (pageTemplateModelElementClass==Renderer.class) + return renderers; + } + return createElementList(pageTemplateModelElementClass); + } + + @SuppressWarnings("unchecked") + private List<PageElement> createElementList(@SuppressWarnings("rawtypes") Class pageTemplateModelElementClass) { + List<PageElement> filteredElements=new ArrayList<>(); + for (PageElement element : elements) { + if (pageTemplateModelElementClass.isAssignableFrom(element.getClass())) + filteredElements.add(element); + else if (element instanceof AbstractChoice) + if (((AbstractChoice)element).isChoiceBetween(pageTemplateModelElementClass)) + filteredElements.add(element); + } + return Collections.unmodifiableList(filteredElements); + } + + /** Returns the choice of ways to sort immediate children in this, or empty meaning sort by default order (relevance) */ + public Sorting getOrder() { return order; } + + public void setOrder(Sorting order) { + ensureNotFrozen(); + this.order=order; + } + + /** Returns max number of (immediate) elements/sections permissible within this, -1 means unrestricted. Default: -1. */ + public int getMax() { return max; } + + public void setMax(int max) { + ensureNotFrozen(); + this.max=max; + } + + /** Returns min number of (immediate) elements/sections desired within this, -1 means unrestricted. Default: -1. */ + public int getMin() { return min; } + + public void setMin(int min) { + ensureNotFrozen(); + this.min=min; + } + + public @Override void freeze() { + if (isFrozen()) return; + + for (PageElement element : elements) + element.freeze(); + elements=Collections.unmodifiableList(elements); + sections=createElementList(Section.class); + sources=createElementList(Source.class); + renderers=createElementList(Renderer.class); + + super.freeze(); + } + + /** Accepts a visitor to this structure */ + public @Override void accept(PageTemplateVisitor visitor) { + visitor.visit(this); + for (PageElement element : elements) + element.accept(visitor); + } + + public @Override String toString() { + if (id==null || id.isEmpty()) return "a section"; + return "section '" + id + "'"; + } + +} diff --git a/container-search/src/main/java/com/yahoo/search/pagetemplates/model/Source.java b/container-search/src/main/java/com/yahoo/search/pagetemplates/model/Source.java new file mode 100644 index 00000000000..91c403eae84 --- /dev/null +++ b/container-search/src/main/java/com/yahoo/search/pagetemplates/model/Source.java @@ -0,0 +1,137 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.search.pagetemplates.model; + +import com.yahoo.component.provider.FreezableClass; +import com.yahoo.protect.Validator; + +import java.util.*; + +/** + * A source mentioned in a page template. + * <p> + * Two sources are equal if they have the same name and parameters. + * + * @author <a href="mailto:bratseth@yahoo-inc.com">Jon Bratseth</a> + */ +public class Source extends FreezableClass implements PageElement { + + /** The "any" source - used to mark that any source is acceptable here */ + public static final Source any=new Source("*",true); + + /** The obligatory name of a source */ + private String name; + + private List<PageElement> renderers =new ArrayList<>(); + + private Map<String,String> parameters =new LinkedHashMap<>(); + + private String url; + + /** The precalculated hashCode of this object, or 0 if this is not frozen */ + private int hashCode=0; + + public Source(String name) { + this(name,false); + } + + /** Creates a source and optionally immediately freezes it */ + private Source(String name,boolean freeze) { + setName(name); + if (freeze) + freeze(); + } + + /** Returns the name of this source (never null) */ + public String getName() { return name; } + + public final void setName(String name) { + ensureNotFrozen(); + Validator.ensureNotNull("Source name",name); + this.name=name; + } + + /** Returns the url of this source or null if none */ + public String getUrl() { return url; } + + /** + * Sets the url of this source. If a source has an url (i.e this returns non-null), the content of + * the url is <i>not</i> fetched - fetching is left to the frontend by exposing this url in the result. + */ + public void setUrl(String url) { + ensureNotFrozen(); + this.url=url; + } + + /** + * Returns the renderers or choices of renderers to apply on individual items of this source + * <p> + * If this contains multiple renderers/choices, they are to be used on different types of hits returned by this source. + */ + public List<PageElement> renderers() { return renderers; } + + /** + * Returns the parameters of this source as a live reference (never null). + * The parameters will be passed to the provider getting source data. + */ + public Map<String,String> parameters() { return parameters; } + + public @Override void freeze() { + if (isFrozen()) return; + for (PageElement element : renderers) { + if (element instanceof Renderer) { + assignRendererForIfNotSet((Renderer)element); + } + else if (element instanceof Choice) { + for (List<PageElement> renderersAlternative : ((Choice)element).alternatives()) { + for (PageElement rendererElement : renderersAlternative) { + Renderer renderer=(Renderer)rendererElement; + if (renderer.getRendererFor()==null) + renderer.setRendererFor(name); + } + } + } + element.freeze(); + } + parameters = Collections.unmodifiableMap(parameters); + hashCode=hashCode(); + super.freeze(); + } + + private void assignRendererForIfNotSet(Renderer renderer) { + if (renderer.getRendererFor()==null) + renderer.setRendererFor(name); + } + + /** Accepts a visitor to this structure */ + public @Override void accept(PageTemplateVisitor visitor) { + visitor.visit(this); + for (PageElement renderer : renderers) + renderer.accept(visitor); + } + + public @Override int hashCode() { + if (isFrozen()) return hashCode; + int hashCode=name.hashCode(); + int i=0; + for (Map.Entry<String,String> parameter : parameters.entrySet()) + hashCode+=i*17*parameter.getKey().hashCode()+i*31*parameter.getValue().hashCode(); + return hashCode; + } + + public @Override boolean equals(Object other) { + if (other==this) return true; + if (! (other instanceof Source)) return false; + Source otherSource=(Source)other; + if (! this.name.equals(otherSource.name)) return false; + if (this.parameters.size() != otherSource.parameters.size()) return false; + for (Map.Entry<String,String> thisParameter : this.parameters.entrySet()) + if ( ! thisParameter.getValue().equals(otherSource.parameters.get(thisParameter.getKey()))) + return false; + return true; + } + + public @Override String toString() { + return "source '" + name + "'"; + } + +} diff --git a/container-search/src/main/java/com/yahoo/search/pagetemplates/model/package-info.java b/container-search/src/main/java/com/yahoo/search/pagetemplates/model/package-info.java new file mode 100644 index 00000000000..22a004d7555 --- /dev/null +++ b/container-search/src/main/java/com/yahoo/search/pagetemplates/model/package-info.java @@ -0,0 +1,7 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +@ExportPackage +@PublicApi +package com.yahoo.search.pagetemplates.model; + +import com.yahoo.api.annotations.PublicApi; +import com.yahoo.osgi.annotation.ExportPackage; |