Fix FindBugs violations
[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 =
25         (o1, o2) -> o1.getID().compareTo(o2.getID());
26
27     public static class PaginationResults<T extends INeutronObject<T>> {
28         List<T> collection;
29         List<NeutronPageLink> links;
30
31         public PaginationResults(List<T> collection, List<NeutronPageLink> links) {
32             this.collection = collection;
33             this.links = links;
34         }
35     }
36
37     private static final class MarkerObject extends NeutronObject<MarkerObject> {
38         private static final long serialVersionUID = 1L;
39
40         private final String id;
41
42         MarkerObject(String id) {
43             this.id = id;
44         }
45
46         @Override
47         public String getID() {
48             return id;
49         }
50
51         @Override
52         public void setID(String newId) {
53             throw new UnsupportedOperationException("Marker has constant ID");
54         }
55
56         @Override
57         public MarkerObject extractFields(List<String> fields) {
58             throw new UnsupportedOperationException("extractFields shouldn't be called for MarkerObject");
59         }
60     }
61
62     private PaginatedRequestFactory() {
63     }
64
65     /*
66      * SuppressWarnings is needed because the compiler does not understand that we
67      * are actually safe here.
68      *
69      * FIXME: the only caller performs a cast back, so this is not actually necessary.
70      */
71     @SuppressWarnings("unchecked")
72     public static <T extends INeutronObject<T>> INeutronRequest<T> createRequest(Integer limit, String marker,
73             Boolean pageReverse, UriInfo uriInfo, List<T> collection, Class<T> clazz) {
74         PaginationResults<T> results = paginate(limit, marker, pageReverse, uriInfo, collection);
75
76         if (clazz.equals(NeutronNetwork.class)) {
77             return (INeutronRequest<T>) new NeutronNetworkRequest((List<NeutronNetwork>) results.collection,
78                     results.links);
79         }
80         if (clazz.equals(NeutronSubnet.class)) {
81             return (INeutronRequest<T>) new NeutronSubnetRequest((List<NeutronSubnet>) results.collection,
82                     results.links);
83         }
84         if (clazz.equals(NeutronPort.class)) {
85             return (INeutronRequest<T>) new NeutronPortRequest((List<NeutronPort>) results.collection, results.links);
86         }
87         return null;
88     }
89
90     private static <T extends INeutronObject<T>> PaginationResults<T> paginate(Integer limit, String marker,
91             Boolean pageReverse, UriInfo uriInfo, List<T> collection) {
92         List<NeutronPageLink> links = new ArrayList<>();
93         final int startPos;
94         String startMarker;
95         String endMarker;
96         Boolean firstPage;
97         Boolean lastPage = false;
98
99         Collections.sort(collection, NEUTRON_OBJECT_COMPARATOR);
100
101         if (marker != null) {
102             int offset = Collections.binarySearch(collection, new MarkerObject(marker), NEUTRON_OBJECT_COMPARATOR);
103             if (offset < 0) {
104                 throw new ResourceNotFoundException("UUID for marker: " + marker + " could not be found");
105             }
106
107             if (!pageReverse) {
108                 startPos = offset + 1;
109             } else {
110                 startPos = offset - limit;
111             }
112         } else {
113             startPos = 0;
114         }
115
116         firstPage = startPos == 0;
117
118         if (startPos + limit >= collection.size()) {
119             collection = collection.subList(startPos, collection.size());
120             startMarker = collection.get(0).getID();
121             endMarker = collection.get(collection.size() - 1).getID();
122             lastPage = true;
123         } else if (startPos < 0) {
124             if (startPos + limit > 0) {
125                 collection = collection.subList(0, startPos + limit);
126                 startMarker = collection.get(0).getID();
127                 endMarker = collection.get(collection.size() - 1).getID();
128                 firstPage = true;
129             } else {
130                 throw new BadRequestException(
131                         "Requested page is out of bounds. Please check the supplied limit and marker");
132             }
133         } else {
134             collection = collection.subList(startPos, startPos + limit);
135             startMarker = collection.get(0).getID();
136             endMarker = collection.get(limit - 1).getID();
137         }
138
139         if (!lastPage) {
140             NeutronPageLink next = new NeutronPageLink();
141             next.setRef("next");
142             next.setHref(uriInfo.getAbsolutePath().toString() + "?limit=" + limit.toString() + "&marker=" + endMarker);
143             links.add(next);
144         }
145
146         if (!firstPage) {
147             NeutronPageLink previous = new NeutronPageLink();
148             previous.setRef("previous");
149             previous.setHref(uriInfo.getAbsolutePath().toString() + "?limit=" + limit.toString() + "&marker="
150                     + startMarker + "&page_reverse=True");
151             links.add(previous);
152         }
153
154         return new PaginationResults<>(collection, links);
155     }
156 }