blob: b5e81addb9470094a2a98c2be90b45a24271daa5 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache 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.apache.org/licenses/LICENSE-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.waveprotocol.box.server.waveserver;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.waveprotocol.box.server.util.WaveletDataUtil;
import org.waveprotocol.wave.model.id.IdConstants;
import org.waveprotocol.wave.model.id.IdUtil;
import org.waveprotocol.wave.model.id.WaveId;
import org.waveprotocol.wave.model.id.WaveletId;
import org.waveprotocol.wave.model.id.WaveletName;
import org.waveprotocol.wave.model.wave.ParticipantId;
import org.waveprotocol.wave.model.wave.ParticipantIdUtil;
import org.waveprotocol.wave.model.wave.data.ObservableWaveletData;
import org.waveprotocol.wave.model.wave.data.ReadableWaveletData;
import org.waveprotocol.wave.model.wave.data.WaveViewData;
import org.waveprotocol.wave.model.wave.data.impl.WaveViewDataImpl;
import org.waveprotocol.wave.util.logging.Log;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Set;
/**
* Base implementation of search provider.
*
* @author yurize@apache.org (Yuri Zelikov)
*/
public abstract class AbstractSearchProviderImpl implements SearchProvider {
private static final Log LOG = Log.get(AbstractSearchProviderImpl.class);
protected final WaveDigester digester;
protected final ParticipantId sharedDomainParticipantId;
private final WaveMap waveMap;
public AbstractSearchProviderImpl(final String waveDomain, WaveDigester digester, WaveMap waveMap) {
this.digester = digester;
sharedDomainParticipantId = ParticipantIdUtil.makeUnsafeSharedDomainParticipantId(waveDomain);
this.waveMap = waveMap;
}
protected List<WaveViewData> computeSearchResult(final ParticipantId user, int startAt,
int numResults, List<WaveViewData> results) {
int searchResultSize = results.size();
// Check if we have enough results to return.
if (searchResultSize < startAt) {
return Collections.emptyList();
} else {
int endAt = Math.min(startAt + numResults, searchResultSize);
return Lists.newArrayList(results.subList(startAt, endAt));
}
}
// TODO (yurize) : Refactor this method. It does two things: filtering and
// building waves.
protected LinkedHashMap<WaveId, WaveViewData> filterWavesViewBySearchCriteria(
Function<ReadableWaveletData, Boolean> matchesFunction,
LinkedHashMultimap<WaveId, WaveletId> currentUserWavesView) {
// Must use a map with stable ordering, since indices are meaningful.
LinkedHashMap<WaveId, WaveViewData> results = Maps.newLinkedHashMap();
// Loop over the user waves view.
for (WaveId waveId : currentUserWavesView.keySet()) {
Set<WaveletId> waveletIds = currentUserWavesView.get(waveId);
WaveViewData view = buildWaveViewData(waveId, waveletIds, matchesFunction, waveMap);
Iterable<? extends ObservableWaveletData> wavelets = view.getWavelets();
boolean hasConversation = false;
for (ObservableWaveletData waveletData : wavelets) {
if (IdUtil.isConversationalId(waveletData.getWaveletId())) {
hasConversation = true;
break;
}
}
if ((view != null) && hasConversation) {
results.put(waveId, view);
}
}
return results;
}
public static WaveViewData buildWaveViewData(WaveId waveId, Set<WaveletId> waveletIds,
Function<ReadableWaveletData, Boolean> matchesFunction, WaveMap waveMap) {
WaveViewData view = WaveViewDataImpl.create(waveId); // Copy of the wave built up for search hits.
for (WaveletId waveletId : waveletIds) {
WaveletContainer waveletContainer = null;
WaveletName waveletname = WaveletName.of(waveId, waveletId);
// TODO (Yuri Z.) This loop collects all the wavelets that match the
// query, so the view is determined by the query. Instead we should
// look at the user's wave view and determine if the view matches the
// query.
try {
waveletContainer = waveMap.getWavelet(waveletname);
if ((waveletContainer == null) || !waveletContainer.applyFunction(matchesFunction)) {
continue;
}
// Just keep adding all the relevant wavelets in this wave.
view.addWavelet(waveletContainer.copyWaveletData());
} catch (WaveletStateException e) {
LOG.warning("Failed to access wavelet " + waveletContainer.getWaveletName(), e);
}
}
return view;
}
/**
* Verifies whether the wavelet matches the filter criteria.
*
* @param wavelet the wavelet.
* @param user the logged in user.
* @param sharedDomainParticipantId the shared domain participant id.
* @param isAllQuery true if the search results should include shared for this
* domain waves.
*/
protected boolean isWaveletMatchesCriteria(ReadableWaveletData wavelet, ParticipantId user,
ParticipantId sharedDomainParticipantId, boolean isAllQuery)
throws WaveletStateException {
Preconditions.checkNotNull(wavelet);
// If it is user data wavelet for the user - return true.
if (IdUtil.isUserDataWavelet(wavelet.getWaveletId()) && wavelet.getCreator().equals(user)) {
return true;
}
// The wavelet should have logged in user as participant for 'in:inbox'
// query.
if (!isAllQuery && !wavelet.getParticipants().contains(user)) {
return false;
}
// Or if it is an 'all' query - then either logged in user or shared domain
// participant should be present in the wave.
if (isAllQuery
&& !WaveletDataUtil.checkAccessPermission(wavelet, user, sharedDomainParticipantId)) {
return false;
}
// If not returned 'false' above - then logged in user is either
// explicit or implicit participant and therefore has access permission.
return true;
}
/**
* Ensures that each wave in the current waves view has the user data wavelet by always adding
* it to the view.
*/
protected void ensureWavesHaveUserDataWavelet(
LinkedHashMultimap<WaveId, WaveletId> currentUserWavesView, ParticipantId user) {
WaveletId udw =
WaveletId.of(user.getDomain(),
IdUtil.join(IdConstants.USER_DATA_WAVELET_PREFIX, user.getAddress()));
Set<WaveId> waveIds = currentUserWavesView.keySet();
for (WaveId waveId : waveIds) {
Set<WaveletId> waveletIds = currentUserWavesView.get(waveId);
waveletIds.add(udw);
}
}
}