Java tutorial
/** * Copyright 2009, 2010 The Regents of the University of California * Licensed under the Educational Community License, Version 2.0 * (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.osedu.org/licenses/ECL-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing * permissions and limitations under the License. * */ package org.opencastproject.episode.impl; import org.apache.commons.lang.StringUtils; import org.apache.solr.client.solrj.SolrQuery; import org.apache.solr.common.SolrDocument; import org.opencastproject.episode.api.JaxbMediaSegment; import org.opencastproject.episode.api.JaxbSearchResult; import org.opencastproject.episode.api.JaxbSearchResultItem; import org.opencastproject.episode.api.MediaSegment; import org.opencastproject.episode.api.SearchResult; import org.opencastproject.episode.api.SearchResultItem; import org.opencastproject.episode.api.Version; import org.opencastproject.episode.impl.solr.DField; import org.opencastproject.episode.impl.solr.Schema; import org.opencastproject.mediapackage.MediaPackage; import org.opencastproject.mediapackage.MediaPackageException; import org.opencastproject.mediapackage.MediaPackageParser; import org.opencastproject.util.data.Function; import org.opencastproject.util.data.Function0; import org.opencastproject.util.data.Option; import org.opencastproject.util.data.Predicate; import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.regex.Matcher; import java.util.regex.Pattern; import static org.opencastproject.util.ReflectionUtil.xfer; import static org.opencastproject.util.data.Monadics.mlist; import static org.opencastproject.util.data.Option.option; import static org.opencastproject.util.data.functions.Misc.chuck; /** Functions to convert between data types. */ public final class Convert { private Convert() { } /** {@link #convert(org.opencastproject.episode.api.SearchResultItem)} as a function. */ public static final Function<SearchResultItem, JaxbSearchResultItem> convertToJaxbSearchResultItem = new Function<SearchResultItem, JaxbSearchResultItem>() { @Override public JaxbSearchResultItem apply(SearchResultItem item) { return convert(item); } }; /** Convert a {@link SearchResultItem} into its JAXB representation. */ public static JaxbSearchResultItem convert(final SearchResultItem item) { if (item instanceof JaxbSearchResultItem) return (JaxbSearchResultItem) item; return xfer(new JaxbSearchResultItem(), SearchResultItem.class, new Function<JaxbSearchResultItem, SearchResultItem>() { @Override public SearchResultItem apply(final JaxbSearchResultItem conv) { return new SearchResultItem() { @Override public String getId() { conv.setId(item.getId()); return null; } @Override public MediaPackage getMediaPackage() { conv.setMediaPackage(item.getMediaPackage()); return null; } @Override public String getOrganization() { conv.setOrganization(item.getOrganization()); return null; } @Override public long getDcExtent() { conv.setDcExtent(item.getDcExtent()); return 0; } @Override public String getDcTitle() { conv.setDcTitle(item.getDcTitle()); return null; } @Override public String getDcSeriesTitle() { conv.setDcSeriesTitle(item.getDcSeriesTitle()); return null; } @Override public String getDcSubject() { conv.setDcSubject(item.getDcSubject()); return null; } @Override public String getDcDescription() { conv.setDcDescription(item.getDcDescription()); return null; } @Override public String getDcCreator() { conv.setDcCreator(item.getDcCreator()); return null; } @Override public String getDcPublisher() { conv.setDcPublisher(item.getDcPublisher()); return null; } @Override public String getDcContributor() { conv.setDcContributor(item.getDcContributor()); return null; } @Override public String getDcAbstract() { conv.setDcAbstract(item.getDcAbstract()); return null; } @Override public Date getDcCreated() { conv.setDcCreated(item.getDcCreated()); return null; } @Override public Date getDcAvailableFrom() { conv.setDcAvailableFrom(item.getDcAvailableFrom()); return null; } @Override public Date getDcAvailableTo() { conv.setDcAvailableTo(item.getDcAvailableTo()); return null; } @Override public String getDcLanguage() { conv.setDcLanguage(item.getDcLanguage()); return null; } @Override public String getDcRightsHolder() { conv.setDcRightsHolder(item.getDcRightsHolder()); return null; } @Override public String getDcSpatial() { conv.setDcSpatial(item.getDcSpatial()); return null; } @Override public String getDcIsPartOf() { conv.setDcIsPartOf(item.getDcIsPartOf()); return null; } @Override public String getDcReplaces() { conv.setDcReplaces(item.getDcReplaces()); return null; } @Override public String getDcType() { conv.setDcType(item.getDcType()); return null; } @Override public String getDcAccessRights() { conv.setDcAccessRights(item.getDcAccessRights()); return null; } @Override public String getDcLicense() { conv.setDcLicense(item.getDcLicense()); return null; } @Override public String getOcAcl() { conv.setOcAcl(item.getOcAcl()); return null; } @Override public SearchResultItemType getType() { conv.setMediaType(item.getType()); return null; } @Override public String[] getKeywords() { for (String a : item.getKeywords()) conv.addKeyword(a); return null; } @Override public String getCover() { conv.setCover(item.getCover()); return null; } @Override public Date getTimestamp() { conv.setTimestamp(item.getTimestamp()); return null; } @Override public double getScore() { conv.setScore(item.getScore()); return 0; } @Override public MediaSegment[] getSegments() { for (MediaSegment a : item.getSegments()) conv.addSegment(a); return null; } @Override public Option<Date> getOcDeleted() { conv.setOcDeleted(item.getOcDeleted()); return null; } @Override public Version getOcVersion() { conv.setOcVersion(item.getOcVersion()); return null; } @Override public boolean isOcLatestVersion() { conv.setOcLatestVersion(item.isOcLatestVersion()); return false; } }; } }); } /** Convert a {@link SolrDocument} into a {@link org.opencastproject.episode.api.JaxbSearchResultItem}. */ public static JaxbSearchResultItem convert(final SolrDocument doc, final SolrQuery query) { return xfer(new JaxbSearchResultItem(), SearchResultItem.class, new Function<JaxbSearchResultItem, SearchResultItem>() { @Override public SearchResultItem apply(final JaxbSearchResultItem conv) { return new SearchResultItem() { private final String dfltString = null; @Override public String getId() { conv.setId(Schema.getDcId(doc)); return null; } @Override public String getOrganization() { conv.setOrganization(Schema.getOrganization(doc)); return null; } @Override public MediaPackage getMediaPackage() { try { conv.setMediaPackage( MediaPackageParser.getFromXml(Schema.getOcMediapackage(doc))); } catch (MediaPackageException e) { return chuck(e); } return null; } @Override public long getDcExtent() { final long extent = new Function0<Long>() { @Override public Long apply() { if (SearchResultItemType.AudioVisual.equals(getTypeInternal())) { Long extent = Schema.getDcExtent(doc); if (extent != null) return extent; } return -1L; } }.apply(); conv.setDcExtent(extent); return -1; } private String getLanguageUndefinedOrFirst(final List<DField<String>> fields) { return mlist(fields).find(isLanguageUndefined).map(getValue) .getOrElse(new Function0<String>() { @Override public String apply() { // ... since none is present return the first arbitrary title return Schema.getFirst(fields, dfltString); } }); } @Override public String getDcTitle() { conv.setDcTitle(getLanguageUndefinedOrFirst(Schema.getDcTitle(doc))); return null; } @Override public String getDcSeriesTitle() { conv.setDcSeriesTitle(getLanguageUndefinedOrFirst(Schema.getDcSeriesTitle(doc))); return null; } @Override public String getDcSubject() { conv.setDcSubject(Schema.getFirst(Schema.getDcSubject(doc), dfltString)); return null; } @Override public String getDcDescription() { conv.setDcDescription(Schema.getFirst(Schema.getDcDescription(doc), dfltString)); return null; } @Override public String getDcCreator() { conv.setDcCreator(Schema.getFirst(Schema.getDcCreator(doc), dfltString)); return null; } @Override public String getDcPublisher() { conv.setDcPublisher(Schema.getFirst(Schema.getDcPublisher(doc), dfltString)); return null; } @Override public String getDcContributor() { conv.setDcContributor(Schema.getFirst(Schema.getDcContributor(doc), dfltString)); return null; } @Override public String getDcAbstract() { return null; } @Override public Date getDcCreated() { conv.setDcCreated(Schema.getDcCreated(doc)); return null; } @Override public Date getDcAvailableFrom() { conv.setDcAvailableFrom(Schema.getDcAvailableFrom(doc)); return null; } @Override public Date getDcAvailableTo() { conv.setDcAvailableTo(Schema.getDcAvailableTo(doc)); return null; } @Override public String getDcLanguage() { conv.setDcLanguage(Schema.getDcLanguage(doc)); return null; } @Override public String getDcRightsHolder() { conv.setDcRightsHolder(Schema.getFirst(Schema.getDcRightsHolder(doc), dfltString)); return null; } @Override public String getDcSpatial() { conv.setDcSpatial(Schema.getFirst(Schema.getDcSpatial(doc), dfltString)); return null; } @Override public String getDcIsPartOf() { conv.setDcIsPartOf(Schema.getDcIsPartOf(doc)); return null; } @Override public String getDcReplaces() { conv.setDcReplaces(Schema.getDcReplaces(doc)); return null; } @Override public String getDcType() { conv.setDcType(Schema.getDcType(doc)); return null; } @Override public String getDcAccessRights() { conv.setDcAccessRights(Schema.getFirst(Schema.getDcAccessRights(doc), dfltString)); return null; } @Override public String getDcLicense() { conv.setDcLicense(Schema.getFirst(Schema.getDcLicense(doc), dfltString)); return null; } @Override public String getOcAcl() { conv.setOcAcl(Schema.getOcAcl(doc)); return null; } @Override public SearchResultItemType getType() { conv.setMediaType(getTypeInternal()); return null; } private SearchResultItemType getTypeInternal() { String t = Schema.getOcMediatype(doc); return t != null ? SearchResultItemType.valueOf(t) : null; } @Override public String[] getKeywords() { final String[] keywords = new Function0<String[]>() { @Override public String[] apply() { if (SearchResultItemType.AudioVisual.equals(getTypeInternal())) { String k = Schema.getOcKeywords(doc); return k != null ? k.split(" ") : new String[0]; } else return new String[0]; } }.apply(); for (String a : keywords) conv.addKeyword(a); return null; } @Override public String getCover() { conv.setCover(Schema.getOcCover(doc)); return null; } @Override public Date getTimestamp() { conv.setTimestamp(Schema.getOcTimestamp(doc)); return null; } @Override public double getScore() { conv.setScore(Schema.getScore(doc)); return -1; } @Override public MediaSegment[] getSegments() { final MediaSegment[] segments = new Function0<MediaSegment[]>() { @Override public MediaSegment[] apply() { if (SearchResultItemType.AudioVisual.equals(getType())) return createSearchResultSegments(doc, query) .toArray(new JaxbMediaSegment[0]); else return new JaxbMediaSegment[0]; } }.apply(); for (MediaSegment a : segments) conv.addSegment(a); return null; } @Override public Option<Date> getOcDeleted() { conv.setOcDeleted(option(Schema.getOcDeleted(doc))); return null; } @Override public Version getOcVersion() { conv.setOcVersion(Schema.getOcVersion(doc)); return null; } @Override public boolean isOcLatestVersion() { conv.setOcLatestVersion(Schema.isOcLatestVersion(doc)); return false; } }; } }); } /** Convert a {@link SearchResult} into its JAXB representation. */ public static JaxbSearchResult convert(final SearchResult source) { return xfer(new JaxbSearchResult(), SearchResult.class, new Function<JaxbSearchResult, SearchResult>() { @Override public SearchResult apply(final JaxbSearchResult jaxb) { return new SearchResult() { @Override public List<SearchResultItem> getItems() { jaxb.setItems(mlist(source.getItems()).map(convertToJaxbSearchResultItem).value()); return null; } @Override public String getQuery() { jaxb.setQuery(source.getQuery()); return null; } @Override public long size() { return 0; // size is calculated from items list } @Override public long getTotalSize() { jaxb.setTotalSize(source.getTotalSize()); return 0; } @Override public long getOffset() { jaxb.setOffset(source.getOffset()); return 0; } @Override public long getLimit() { jaxb.setLimit(source.getLimit()); return 0; } @Override public long getSearchTime() { jaxb.setSearchTime(source.getSearchTime()); return 0; } @Override public long getPage() { return 0; // calculated } }; } }); } // /** * Creates a list of <code>MediaSegment</code>s from the given result document. * * @param doc * the result document * @param query * the original query */ private static List<JaxbMediaSegment> createSearchResultSegments(SolrDocument doc, SolrQuery query) { List<JaxbMediaSegment> segments = new ArrayList<JaxbMediaSegment>(); // The maximum number of hits in a segment int maxHits = 0; // Loop over every segment for (String fieldName : doc.getFieldNames()) { if (!fieldName.startsWith(Schema.SEGMENT_TEXT_PREFIX)) continue; // Ceate a new segment int segmentId = Integer.parseInt(fieldName.substring(Schema.SEGMENT_TEXT_PREFIX.length())); JaxbMediaSegment segment = new JaxbMediaSegment(segmentId); segment.setText(mkString(doc.getFieldValue(fieldName))); // Read the hints for this segment Properties segmentHints = new Properties(); try { String hintFieldName = Schema.SEGMENT_HINT_PREFIX + segment.getIndex(); Object hintFieldValue = doc.getFieldValue(hintFieldName); segmentHints.load(new ByteArrayInputStream(hintFieldValue.toString().getBytes())); } catch (IOException e) { } // get segment time String segmentTime = segmentHints.getProperty("time"); if (segmentTime == null) throw new IllegalStateException("Found segment without time hint"); segment.setTime(Long.parseLong(segmentTime)); // get segment duration String segmentDuration = segmentHints.getProperty("duration"); if (segmentDuration == null) throw new IllegalStateException("Found segment without duration hint"); segment.setDuration(Long.parseLong(segmentDuration)); // get preview urls for (Map.Entry<Object, Object> entry : segmentHints.entrySet()) { if (entry.getKey().toString().startsWith("preview.")) { String[] parts = entry.getKey().toString().split("\\."); segment.addPreview(entry.getValue().toString(), parts[1]); } } // calculate the segment's relevance with respect to the query String queryText = query.getQuery(); String segmentText = segment.getText(); if (!StringUtils.isBlank(queryText) && !StringUtils.isBlank(segmentText)) { segmentText = segmentText.toLowerCase(); Pattern p = Pattern.compile(".*fulltext:\\(([^)]*)\\).*"); Matcher m = p.matcher(queryText); if (m.matches()) { String[] queryTerms = StringUtils.split(m.group(1).toLowerCase()); int segmentHits = 0; int textLength = segmentText.length(); for (String t : queryTerms) { int startIndex = 0; while (startIndex < textLength - 1) { int foundAt = segmentText.indexOf(t, startIndex); if (foundAt < 0) break; segmentHits++; startIndex = foundAt + t.length(); } } // for now, just store the number of hits, but keep track of the maximum hit count segment.setRelevance(segmentHits); if (segmentHits > maxHits) maxHits = segmentHits; } } segments.add(segment); } for (JaxbMediaSegment segment : segments) { int hitsInSegment = segment.getRelevance(); if (hitsInSegment > 0) segment.setRelevance((int) ((100 * hitsInSegment) / maxHits)); } return segments; } /** * Simple helper method to avoid null strings. * * @param f * object which implements <code>toString()</code> method. * @return The input object or empty string. */ private static String mkString(Object f) { if (f != null) return f.toString(); else return ""; } private static Function<DField<String>, Boolean> isLanguageUndefined = new Predicate<DField<String>>() { @Override public Boolean apply(DField<String> f) { return f.getSuffix().equals(Schema.LANGUAGE_UNDEFINED); } }; private static Function<DField<String>, String> getValue = new Function<DField<String>, String>() { @Override public String apply(DField<String> f) { return f.getValue(); } }; }