package com.smash.revolance.ui.model.page.api; /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * Revolance-UI-Model * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * Copyright (C) 2012 - 2013 RevoLance * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program. If not, see * <http://www.gnu.org/licenses/gpl-3.0.html>. * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ import com.smash.revolance.ui.model.diff.DiffType; import com.smash.revolance.ui.model.diff.ElementDiffType; import com.smash.revolance.ui.model.diff.PageDiffType; import com.smash.revolance.ui.model.element.ElementNotFound; import com.smash.revolance.ui.model.element.api.ElementBean; import com.smash.revolance.ui.model.user.UserBean; import org.apache.commons.lang3.StringUtils; import org.codehaus.jackson.annotate.JsonAutoDetect; import org.codehaus.jackson.annotate.JsonIgnore; import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.map.annotate.JsonDeserialize; import java.io.IOException; import java.util.*; /** * User: wsmash * Date: 21/04/13 * Time: 09:33 */ @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE) public class PageBean { private String id = ""; private String title = ""; private String url = ""; @JsonDeserialize(contentAs = ElementBean.class, as = ArrayList.class) private List<ElementBean> content = new ArrayList<ElementBean>(); private String caption = ""; private int w, h; private String originalPageId; @JsonIgnore private PageBean original; @JsonDeserialize(contentAs = PageBean.class, as = ArrayList.class) private List<PageBean> variants = new ArrayList<PageBean>(); private ElementBean source; private boolean broken; private boolean browsed; private boolean homePage; private boolean loginPage; private boolean external; private boolean authorized = true; @JsonIgnore private UserBean user; @JsonIgnore private Page instance; private Map<DiffType, List<ElementBean>> variations; private String scrollY; private String scrollX; private Map<String, String> metas = new HashMap<>(); public PageBean() { } public PageBean(Page instance) { this(); setInstance(instance); } public void setTitle(String title) { this.title = title; } public String getTitle() { return title; } public void setLinks(ArrayList<ElementBean> links) { this.content.addAll(links); Collections.sort(content); } public List<ElementBean> getLinks() { return ElementBean.filterLinks(getContent()); } public void setButtons(ArrayList<ElementBean> buttons) { this.content.addAll(buttons); Collections.sort(content); } public List<ElementBean> getButtons() { return ElementBean.filterButtons(getContent()); } public void setUrl(String url) { this.url = url; } public void setImages(List<ElementBean> images) { this.content.addAll(images); Collections.sort(content); } public List<ElementBean> getImages() { return ElementBean.filterImages(getContent()); } public String getUrl() { return url; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getCaption() { return caption; } public void setCaption(String caption) { this.caption = caption; } public boolean isOriginal() { return this == original; } public ElementBean getSource() { return source; } public void setSource(ElementBean source) { this.source = source; } public PageBean getOriginal() { return original; } public void setAuthorized(boolean authorized) { this.authorized = authorized; } public boolean isAuthorized() { return authorized; } public List<PageBean> getVariants() { return variants; } public void setVariants(ArrayList<PageBean> variants) { this.variants = variants; } public void setInstance(Page instance) { this.instance = instance; } public Page getInstance() { return instance; } public void setOriginal(PageBean original) { this.original = original; this.originalPageId = original.getId(); } public String getOriginalPageId() { return originalPageId; } public boolean isBroken() { return broken; } public boolean hasBeenBrowsed() { return browsed; } public void setBrowsed(boolean browsed) { this.browsed = browsed; } public boolean isHome() { return homePage; } public void setHome(boolean b) { this.homePage = b; } public void setUser(UserBean user) { this.user = user; } public UserBean getUser() { return user; } public void setHeight(int height) { this.h = height; } public void setWidth(int width) { this.w = width; } public String toJSon() throws Exception { ObjectMapper mapper = new ObjectMapper(); return mapper.writeValueAsString( this ); } /** * Handling variation of a page with dynamic content poping up vs original content * * @return * @throws Exception */ public boolean hasVariations() throws Exception { return ( getAddedVariations().size() > 0 || getRemovedVariations().size() > 0 ) && !getCaption().contentEquals( getOriginal().getCaption() ); } /** * Handling variation of a page with dynamic content poping up vs original content * * @return * @throws Exception */ public List<ElementBean> getAddedVariations() throws Exception { return getVariations().get( DiffType.ADDED ); } /** * Handling variation of a page with dynamic content poping up vs original content * * @return * @throws Exception */ public List<ElementBean> getRemovedVariations() throws Exception { return getVariations().get( DiffType.DELETED ); } /** * Handling variation of a page with dynamic content poping up vs original content * * @return * @throws Exception */ public List<ElementBean> getBaseVariations() throws Exception { return getVariations().get( DiffType.CHANGED ); } public Map<DiffType, List<ElementBean>> getVariations() throws Exception { if ( variations == null ) { variations = this.getDiff( getOriginal() ); } return variations; } public ArrayList<ElementBean> getRemovedElements(List<ElementBean> elements) throws Exception { ArrayList<ElementBean> removedElements = new ArrayList<ElementBean>(); for ( ElementBean refElement : elements ) { if ( !this.contains( refElement ) ) { removedElements.add( refElement ); } } markElements( removedElements, DiffType.DELETED ); markElements( removedElements, ElementDiffType.values() ); filterElementsIncludedInEachOthers( removedElements ); Collections.sort( removedElements ); return removedElements; } public static void markElements(List<ElementBean> elements, ElementDiffType... diff) throws IOException { for ( ElementBean element : elements ) { markElement( element, diff ); } } public static void markElements(List<ElementBean> elements, DiffType type) { for ( ElementBean element : elements ) { // element.setDiffType( type ); } } public ArrayList<ElementBean> getAddedElements(List<ElementBean> elements) throws Exception { ArrayList<ElementBean> addedElements = new ArrayList<ElementBean>(); for ( ElementBean element : getContent() ) { boolean added = true; for ( ElementBean elem : elements ) { if ( elem.equals( element ) ) { added = false; break; } } if ( added ) { addedElements.add( element ); } } filterElementsIncludedInEachOthers( addedElements ); markElements( addedElements, DiffType.ADDED ); markElements( addedElements, ElementDiffType.values() ); Collections.sort( addedElements ); return addedElements; } public ArrayList<ElementBean> getBaseElements(List<ElementBean> elements) throws Exception { ArrayList<ElementBean> baseElements = new ArrayList<ElementBean>(); for ( ElementBean refElement : elements ) { if ( this.contains( refElement ) ) { baseElements.add( refElement ); } } filterElementsIncludedInEachOthers( baseElements ); markElements( baseElements, DiffType.BASE ); Collections.sort( baseElements ); return baseElements; } public boolean contains(ElementBean element) throws Exception { for ( ElementBean refElement : getContent() ) { if ( refElement.equals( element ) ) { return true; } } return false; } public Map<DiffType, List<ElementBean>> getDiff(List<ElementBean> elements) throws Exception { Map<DiffType, List<ElementBean>> differencies = new HashMap<DiffType, List<ElementBean>>(); ArrayList<ElementBean> baseElements = getBaseElements( elements ); ArrayList<ElementBean> removedElements = getRemovedElements( elements ); ArrayList<ElementBean> addedElements = getAddedElements( elements ); ArrayList<ElementBean> updatedElements = updatedElements( addedElements, removedElements ); baseElements.addAll( updatedElements ); addedElements.removeAll( updatedElements ); removedElements.removeAll( updatedElements ); filterElementsIncludedInEachOthers( baseElements ); Collections.sort( baseElements ); differencies.put( DiffType.CHANGED, baseElements ); filterElementsIncludedInEachOthers( removedElements ); Collections.sort( removedElements ); differencies.put( DiffType.DELETED, removedElements ); filterElementsIncludedInEachOthers( addedElements ); Collections.sort( addedElements ); differencies.put( DiffType.ADDED, addedElements ); return differencies; } private ArrayList<ElementBean> updatedElements(List<ElementBean> addedElements, List<ElementBean> removedElements) { ArrayList<ElementBean> baseElements = new ArrayList<ElementBean>(); int removedIdx = 0; int addedIdx = 0; boolean equalityFound = false; for ( ElementBean element : addedElements ) { for ( ElementBean refElement : removedElements ) { if ( refElement.equalsByAll( element, ElementDiffType.values() ) ) { equalityFound = true; break; } removedIdx++; } if ( equalityFound ) { break; } addedIdx++; } if ( equalityFound ) { ElementBean addedElement = addedElements.remove( addedIdx ); ElementBean removedElement = removedElements.remove( removedIdx ); List<ElementDiffType> diff = addedElement.getDiff( removedElement ); markElement( addedElement, diff.toArray( new ElementDiffType[diff.size()] ) ); baseElements.add( addedElement ); // Check for other elements remaining to be matched baseElements.addAll( updatedElements( addedElements, removedElements ) ); } return baseElements; } public static void markElement(ElementBean element, ElementDiffType... diffTypes) { // element.setElementDiffTypes( Arrays.asList( diffTypes ) ); } public Map<DiffType, List<ElementBean>> getDiff(PageBean page) throws Exception { if ( page == null || this == page ) { return new HashMap<DiffType, List<ElementBean>>(); } else { return getDiff( getContent() ); } } private void filterElementsIncludedInEachOthers(List<ElementBean> elements) { if ( elements.size() > 2 ) { List<ElementBean> filteredElements = new ArrayList<ElementBean>(); ElementBean bigElement; while ( ( bigElement = getBiggestArea( elements ) ) != null ) { elements.remove( bigElement ); List<ElementBean> included = new ArrayList<ElementBean>(); for ( ElementBean anElement : elements ) { if ( anElement.isIncluded( bigElement ) ) { included.add( anElement ); } } elements.removeAll( included ); filteredElements.add( bigElement ); } elements.clear(); elements.addAll( filteredElements ); } } private ElementBean getBiggestArea(List<ElementBean> elements) { if ( elements == null ) { return null; } if ( elements.isEmpty() ) { return null; } int maxArea = 0; ElementBean pageElement = null; for ( ElementBean element : elements ) { if ( element.getArea() > maxArea ) { pageElement = element; maxArea = element.getArea(); } } return pageElement; } public List<ElementBean> getContent() { return content; } public List<ElementBean> getOriginalContent() { return getOriginal().getContent(); } public void setUserBean(UserBean user) { this.user = user; } public boolean equals(PageBean page) throws IOException { return equalsBy( page, PageDiffType.values() ); } public boolean equalsBy(PageBean page, PageDiffType... diffTypes) { boolean b = true; for ( PageDiffType diffType : diffTypes ) { switch ( diffType ) { case LAYOUT: b = b && equalsByLayout( page ); break; case LOOK: b = b && equalsByLook( page ); break; case URL: b = b && equalsByUrl( page ); break; case CONTENT: b = b && equalsByContent( page ); break; } } return b; } public boolean equalsByContent(PageBean page) { return contentEquals( page.getContentAsValue(), getContentAsValue(), false ); } private boolean contentEquals(List<String> content, List<String> refContent, boolean ordered) { if ( content.size() != refContent.size() ) { return false; } else { if ( ordered ) { int refContentIdx = 0; for ( String value : refContent ) { if ( !content.get( refContentIdx ).contentEquals( value ) ) { return false; } refContentIdx++; } return true; } else { for ( String value : refContent ) { if ( !content.contains( value ) ) { return false; } } return true; } } } public boolean equalsByUrl(PageBean page) { return getRelativeUrl().contentEquals( page.getRelativeUrl() ); } public boolean equalsByLook(PageBean page) { return caption.contentEquals( page.getCaption() ); } public List<String> getContentAsValue() { Collections.sort( getContent() ); List<String> contentValue = new ArrayList<String>(); for ( ElementBean element : getContent() ) { String value = element.getContent(); if ( !contentValue.contains( value ) ) { contentValue.add( value ); } } return contentValue; } public String getRelativeUrl() { return StringUtils.remove( getUrl(), getUser().getDomain() ); } public boolean equalsByLayout(PageBean page) { return contentEquals( getContentAsValue(), page.getContentAsValue(), true ); } public List<ElementBean> getBrokenLinks() { List<ElementBean> brokenLinks = new ArrayList<ElementBean>(); for ( ElementBean link : getLinks() ) { if ( link.isBroken() ) { brokenLinks.add( link ); } } return brokenLinks; } public boolean hasBrokenLinks() { return !getBrokenLinks().isEmpty(); } public void clearContent() { content.clear(); } public void setContent(List<ElementBean> content) { this.content = content; } public ElementBean contains(String txt, String impl, boolean active) throws ElementNotFound { for ( ElementBean element : content ) { if ( element.getValue().contentEquals( txt ) && element.getImpl().contentEquals( impl ) && element.isDisabled() != active ) { return element; } } throw new ElementNotFound( "Unable to find element in page." ); } public void setBroken(boolean broken) { this.broken = broken; } public boolean isExternal() { return external; } public void setExternal(boolean external) { this.external = external; } public int getWidth() { return w; } public int getHeight() { return h; } public List<ElementBean> getClickableContent() { List<ElementBean> clickables = new ArrayList<ElementBean>(); String impl = ""; for ( ElementBean element : getContent() ) { impl = element.getImpl(); if ( impl.contentEquals( "Link" ) || impl.contains( "Button" ) ) { clickables.add( element ); } } return clickables; } public void setScrollY(String scrollY) { this.scrollY = scrollY; } public String getScrollY() { return scrollY; } public void setScrollX(String scrollX) { this.scrollX = scrollX; } public String getScrollX() { return scrollX; } public boolean hasVariants() { return variants.isEmpty(); } public boolean isLogin() { return loginPage; } public void addMetaInf(String k, String v) { metas.put(k, v); } public Map<String, String> getMetas() { return metas; } }