3a8ea92de2f0560239fff0409dbe8751db9fa4e7
[neutron.git] / northbound-api / src / main / java / org / opendaylight / neutron / northbound / api / PaginatedRequestFactory.java
1 /*
2  * Copyright (c) 2014, 2015 Red Hat, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.neutron.northbound.api;
10
11 import java.util.ArrayList;
12 import java.util.Collections;
13 import java.util.Comparator;
14 import java.util.List;
15 import javax.ws.rs.core.UriInfo;
16 import org.opendaylight.neutron.spi.INeutronObject;
17 import org.opendaylight.neutron.spi.NeutronNetwork;
18 import org.opendaylight.neutron.spi.NeutronObject;
19 import org.opendaylight.neutron.spi.NeutronPort;
20 import org.opendaylight.neutron.spi.NeutronSubnet;
21
22 public final class PaginatedRequestFactory {
23
24     private static final Comparator<INeutronObject> NEUTRON_OBJECT_COMPARATOR = new Comparator<INeutronObject>() {
25         @Override
26         public int compare(INeutronObject o1, INeutronObject o2) {
27             return o1.getID().compareTo(o2.getID());
28         }
29     };
30
31     public static class PaginationResults<T extends INeutronObject<T>> {
32         List<T> collection;
33         List<NeutronPageLink> links;
34
35         public PaginationResults(List<T> collection, List<NeutronPageLink> links) {
36             this.collection = collection;
37             this.links = links;
38         }
39     }
40
41     private static final class MarkerObject extends NeutronObject<MarkerObject>
42             implements INeutronObject<MarkerObject> {
43         private final String id;
44
45         MarkerObject(String id) {
46             super();
47             this.id = id;
48         }
49
50         @Override
51         public String getID() {
52             return id;
53         }
54
55         @Override
56         public void setID(String id) {
57             throw new UnsupportedOperationException("Marker has constant ID");
58         }
59
60         @Override
61         public MarkerObject extractFields(List<String> fields) {
62             throw new UnsupportedOperationException("extractFields shouldn't be called for MarkerObject");
63         }
64     }
65
66     private PaginatedRequestFactory() {
67     }
68
69     /*
70      * SuppressWarnings is needed because the compiler does not understand that we
71      * are actually safe here.
72      *
73      * FIXME: the only caller performs a cast back, so this is not actually necessary.
74      */
75     @SuppressWarnings("unchecked")
76     public static <T extends INeutronObject<T>> INeutronRequest<T> createRequest(Integer limit, String marker,
77             Boolean pageReverse, UriInfo uriInfo, List<T> collection, Class<T> clazz) {
78         PaginationResults<T> results = paginate(limit, marker, pageReverse, uriInfo, collection);
79
80         if (clazz.equals(NeutronNetwork.class)) {
81             return (INeutronRequest<T>) new NeutronNetworkRequest((List<NeutronNetwork>) results.collection,
82                     results.links);
83         }
84         if (clazz.equals(NeutronSubnet.class)) {
85             return (INeutronRequest<T>) new NeutronSubnetRequest((List<NeutronSubnet>) results.collection,
86                     results.links);
87         }
88         if (clazz.equals(NeutronPort.class)) {
89             return (INeutronRequest<T>) new NeutronPortRequest((List<NeutronPort>) results.collection, results.links);
90         }
91         return null;
92     }
93
94     private static <T extends INeutronObject<T>> PaginationResults<T> paginate(Integer limit, String marker,
95             Boolean pageReverse, UriInfo uriInfo, List<T> collection) {
96         List<NeutronPageLink> links = new ArrayList<>();
97         final int startPos;
98         String startMarker;
99         String endMarker;
100         Boolean firstPage;
101         Boolean lastPage = false;
102
103         Collections.sort(collection, NEUTRON_OBJECT_COMPARATOR);
104
105         if (marker != null) {
106             int offset = Collections.binarySearch(collection, new MarkerObject(marker), NEUTRON_OBJECT_COMPARATOR);
107             if (offset < 0) {
108                 throw new ResourceNotFoundException("UUID for marker: " + marker + " could not be found");
109             }
110
111             if (!pageReverse) {
112                 startPos = offset + 1;
113             } else {
114                 startPos = offset - limit;
115             }
116         } else {
117             startPos = 0;
118         }
119
120         firstPage = startPos == 0;
121
122         if (startPos + limit >= collection.size()) {
123             collection = collection.subList(startPos, collection.size());
124             startMarker = collection.get(0).getID();
125             endMarker = collection.get(collection.size() - 1).getID();
126             lastPage = true;
127         } else if (startPos < 0) {
128             if (startPos + limit > 0) {
129                 collection = collection.subList(0, startPos + limit);
130                 startMarker = collection.get(0).getID();
131                 endMarker = collection.get(collection.size() - 1).getID();
132                 firstPage = true;
133             } else {
134                 throw new BadRequestException(
135                         "Requested page is out of bounds. Please check the supplied limit and marker");
136             }
137         } else {
138             collection = collection.subList(startPos, startPos + limit);
139             startMarker = collection.get(0).getID();
140             endMarker = collection.get(limit - 1).getID();
141         }
142
143         if (!lastPage) {
144             NeutronPageLink next = new NeutronPageLink();
145             next.setRef("next");
146             next.setHref(uriInfo.getAbsolutePath().toString() + "?limit=" + limit.toString() + "&marker=" + endMarker);
147             links.add(next);
148         }
149
150         if (!firstPage) {
151             NeutronPageLink previous = new NeutronPageLink();
152             previous.setRef("previous");
153             previous.setHref(uriInfo.getAbsolutePath().toString() + "?limit=" + limit.toString() + "&marker="
154                     + startMarker + "&page_reverse=True");
155             links.add(previous);
156         }
157
158         return new PaginationResults<T>(collection, links);
159     }
160 }