001package jmri.jmrit.operations.locations; 002 003import java.util.ArrayList; 004import java.util.List; 005 006import org.slf4j.Logger; 007import org.slf4j.LoggerFactory; 008 009import jmri.beans.Bean; 010 011/** 012 * Represents a pool of tracks that share their length. 013 * 014 * @author Daniel Boudreau Copyright (C) 2011 015 * @author Gregory Madsen Copyright (C) 2012 016 * 017 */ 018public class Pool extends Bean { 019 020 public static final String LISTCHANGE_CHANGED_PROPERTY = "poolListChange"; // NOI18N 021 public static final String DISPOSE = "poolDispose"; // NOI18N 022 023 private final static Logger log = LoggerFactory.getLogger(Pool.class); 024 025 // stores tracks for this pool 026 protected List<Track> _tracks = new ArrayList<>(); 027 028 protected String _id = ""; 029 030 public String getId() { 031 return _id; 032 } 033 034 protected String _name = ""; 035 036 public String getName() { 037 return _name; 038 } 039 040 public void setName(String name) { 041 String old = _name; 042 _name = name; 043 firePropertyChange("Name", old, name); 044 } 045 046 /** 047 * The number of tracks in this pool. 048 * 049 * @return the number of tracks in this pool. 050 */ 051 public int getSize() { 052 return _tracks.size(); 053 } 054 055 // for combo boxes 056 @Override 057 public String toString() { 058 return _name; 059 } 060 061 public Pool(String id, String name) { 062 super(false); 063 log.debug("New pool ({}) id: {}", name, id); 064 _name = name; 065 _id = id; 066 } 067 068 public void dispose() { 069 firePropertyChange(DISPOSE, null, DISPOSE); 070 } 071 072 /** 073 * Adds a track to this pool 074 * 075 * @param track to be added. 076 */ 077 public void add(Track track) { 078 if (!_tracks.contains(track)) { 079 int oldSize = _tracks.size(); 080 _tracks.add(track); 081 firePropertyChange(LISTCHANGE_CHANGED_PROPERTY, oldSize, _tracks.size()); 082 } 083 } 084 085 /** 086 * Removes a track from this pool 087 * 088 * @param track to be removed. 089 */ 090 public void remove(Track track) { 091 if (_tracks.contains(track)) { 092 int oldSize = _tracks.size(); 093 _tracks.remove(track); 094 firePropertyChange(LISTCHANGE_CHANGED_PROPERTY, oldSize, _tracks.size()); 095 } 096 } 097 098 public List<Track> getTracks() { 099 // Return a copy to protect the internal list 100 return new ArrayList<>(_tracks); 101 } 102 103 public int getTotalLengthTracks() { 104 return getTracks().stream().map(track -> track.getLength()).reduce(0, Integer::sum); 105 } 106 107 /** 108 * Used to determine the maximum available length for a given track 109 * 110 * @param track the track being evaluated 111 * @return maximum track length 112 */ 113 public int getMaxLengthTrack(Track track) { 114 int length = getTotalLengthTracks(); 115 for (Track t : getTracks()) { 116 if (t == track) { 117 continue; 118 } 119 length = length - t.getPoolMinimumLength(); 120 } 121 return length; 122 } 123 124 /** 125 * Request track length from one of the other tracks in this pool. Other 126 * tracks in the same pool may have their length shortened or lengthened by 127 * this operation. 128 * 129 * @param track the track requesting additional length 130 * @param length the length of rolling stock 131 * @return true if successful 132 */ 133 public boolean requestTrackLength(Track track, int length) { 134 // is there a maximum length restriction? 135 if (track.getUsedLength() + track.getReserved() + length > track.getPoolMaximumLength()) { 136 return false; 137 } 138 // only request enough length for the rolling stock to fit 139 int additionalLength = track.getUsedLength() + track.getReserved() + length - track.getLength(); 140 141 for (Track t : getTracks()) { 142 // note that the reserved track length can be either positive or negative 143 if (t != track) { 144 if (t.getUsedLength() + t.getReserved() + additionalLength <= t.getLength() 145 && t.getLength() - additionalLength >= t.getPoolMinimumLength()) { 146 log.debug("Pool ({}) increasing track ({}) length ({}) decreasing ({})", getName(), 147 track.getName(), additionalLength, t.getName()); // NOI18N 148 t.setLength(t.getLength() - additionalLength); 149 track.setLength(track.getLength() + additionalLength); 150 return true; 151 } else { 152 // steal whatever isn't being used by this track 153 int available = t.getLength() - (t.getUsedLength() + t.getReserved()); 154 int min = t.getLength() - t.getPoolMinimumLength(); 155 if (min < available) { 156 available = min; 157 } 158 if (available > 0) { 159 // adjust track lengths and reduce the additional length needed 160 log.debug("Pool ({}) incremental increase for track ({}) length ({}) decreasing ({})", 161 getName(), track.getName(), available, t.getName()); // NOI18N 162 t.setLength(t.getLength() - available); 163 track.setLength(track.getLength() + available); 164 additionalLength = additionalLength - available; 165 } 166 } 167 } 168 } 169 return false; 170 } 171 172 /** 173 * Used to determine if the option to use maximum track length in a pool is 174 * enabled. Enabled when there's 3 or more tracks in the pool. 175 * 176 * @return true if maximum track length option is available 177 */ 178 public boolean isMaxLengthOptionEnabled() { 179 // need 3 or more tracks in pool before allowing max track length option 180 if (getSize() > 2) { 181 return true; 182 } 183 return false; 184 } 185 186 public boolean isThereMaxLengthRestrictions() { 187 for (Track t : getTracks()) { 188 if (t.getPoolMaximumLength() != Integer.MAX_VALUE) { 189 return true; 190 } 191 } 192 return false; 193 } 194}