org.apache.wicket.request.mapper.CompoundRequestMapper.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.wicket.request.mapper.CompoundRequestMapper.java

Source

/*
 * 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.apache.wicket.request.mapper;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

import org.apache.wicket.request.IRequestHandler;
import org.apache.wicket.request.IRequestMapper;
import org.apache.wicket.request.Request;
import org.apache.wicket.request.Url;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Thread safe compound {@link IRequestMapper}. The mappers are searched depending on their
 * compatibility score and the orders they were registered. If two or more {@link IRequestMapper}s
 * have the same compatibility score, the last registered mapper has highest priority.
 * 
 * @author igor.vaynberg
 * @author Matej Knopp
 */
public class CompoundRequestMapper implements ICompoundRequestMapper {
    private static final Logger LOG = LoggerFactory.getLogger(CompoundRequestMapper.class);

    static class MapperWithScore implements Comparable<MapperWithScore> {
        private final IRequestMapper mapper;
        private final int compatibilityScore;

        public MapperWithScore(final IRequestMapper mapper, final int compatibilityScore) {
            this.mapper = mapper;
            this.compatibilityScore = compatibilityScore;
        }

        @Override
        public int compareTo(final MapperWithScore o) {
            return (compatibilityScore < o.compatibilityScore ? 1
                    : (compatibilityScore > o.compatibilityScore ? -1 : 0));
        }

        public IRequestMapper getMapper() {
            return mapper;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o)
                return true;
            if (!(o instanceof MapperWithScore))
                return false;

            MapperWithScore that = (MapperWithScore) o;

            if (compatibilityScore != that.compatibilityScore)
                return false;
            return mapper.equals(that.mapper);
        }

        @Override
        public int hashCode() {
            int result = mapper.hashCode();
            result = 31 * result + compatibilityScore;
            return result;
        }

        @Override
        public String toString() {
            return "Mapper: " + mapper.getClass().getName() + "; Score: " + compatibilityScore;
        }
    }

    private final List<IRequestMapper> mappers = new CopyOnWriteArrayList<>();

    @Override
    public CompoundRequestMapper add(final IRequestMapper mapper) {
        mappers.add(0, mapper);
        return this;
    }

    @Override
    public CompoundRequestMapper remove(final IRequestMapper mapper) {
        mappers.remove(mapper);
        return this;
    }

    /**
     * Searches the registered {@link IRequestMapper}s to find one that can map the {@link Request}.
     * Each registered {@link IRequestMapper} is asked to provide its compatibility score. Then the
     * mappers are asked to map the request in order depending on the provided compatibility
     * score.
     * <p>
     * The mapper with highest compatibility score which can map the request is returned.
     * 
     * @param request
     * @return RequestHandler for the request or <code>null</code> if no mapper for the request is
     *         found.
     */
    @Override
    public IRequestHandler mapRequest(final Request request) {
        List<MapperWithScore> list = new ArrayList<>(mappers.size());

        for (IRequestMapper mapper : this) {
            int score = mapper.getCompatibilityScore(request);
            list.add(new MapperWithScore(mapper, score));
        }

        Collections.sort(list);

        if (LOG.isDebugEnabled()) {
            logMappers(list, request.getUrl().toString());
        }

        for (MapperWithScore mapperWithScore : list) {
            IRequestMapper mapper = mapperWithScore.getMapper();
            IRequestHandler handler = mapper.mapRequest(request);
            if (handler != null) {
                return handler;
            }
        }

        return null;
    }

    /**
     * Logs all mappers with a positive compatibility score
     *
     * @param mappersWithScores
     *      the list of all mappers
     * @param url
     *      the url to match by these mappers
     */
    private void logMappers(final List<MapperWithScore> mappersWithScores, final String url) {
        final List<MapperWithScore> compatibleMappers = new ArrayList<>();
        for (MapperWithScore mapperWithScore : mappersWithScores) {
            if (mapperWithScore.compatibilityScore > 0) {
                compatibleMappers.add(mapperWithScore);
            }
        }
        if (compatibleMappers.size() == 0) {
            LOG.debug("No compatible mapper found for URL '{}'", url);
        } else if (compatibleMappers.size() == 1) {
            LOG.debug("One compatible mapper found for URL '{}' -> '{}'", url, compatibleMappers.get(0));
        } else {
            LOG.debug("Multiple compatible mappers found for URL '{}'", url);
            for (MapperWithScore compatibleMapper : compatibleMappers) {
                LOG.debug(" * {}", compatibleMapper);
            }
        }
    }

    /**
     * Searches the registered {@link IRequestMapper}s to find one that can map the
     * {@link IRequestHandler}. Each registered {@link IRequestMapper} is asked to map the
     * {@link IRequestHandler} until a mapper which can map the {@link IRequestHandler} is found or
     * no more mappers are left.
     * <p>
     * The mappers are searched in reverse order as they have been registered. More recently
     * registered mappers have bigger priority.
     * 
     * @param handler
     * @return Url for the handler or <code>null</code> if no mapper for the handler is found.
     */
    @Override
    public Url mapHandler(final IRequestHandler handler) {
        for (IRequestMapper mapper : this) {
            Url url = mapper.mapHandler(handler);
            if (url != null) {
                return url;
            }
        }
        return null;
    }

    /**
     * The scope of the compound mapper is the highest score of the registered mappers.
     * 
     * {@inheritDoc}
     */
    @Override
    public int getCompatibilityScore(final Request request) {
        int score = Integer.MIN_VALUE;
        for (IRequestMapper mapper : this) {
            score = Math.max(score, mapper.getCompatibilityScore(request));
        }
        return score;
    }

    @Override
    public Iterator<IRequestMapper> iterator() {
        return mappers.iterator();
    }
}