001/* ***** BEGIN LICENSE BLOCK *****
002 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
003 *
004 * The contents of this file are subject to the Mozilla Public License Version
005 * 1.1 (the "License"); you may not use this file except in compliance with
006 * the License. You may obtain a copy of the License at
007 * http://www.mozilla.org/MPL/
008 *
009 * Software distributed under the License is distributed on an "AS IS" basis,
010 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
011 * for the specific language governing rights and limitations under the
012 * License.
013 *
014 * The Original Code is part of dcm4che, an implementation of DICOM(TM) in
015 * Java(TM), hosted at https://github.com/gunterze/dcm4che.
016 *
017 * The Initial Developer of the Original Code is
018 * Agfa Healthcare.
019 * Portions created by the Initial Developer are Copyright (C) 2011
020 * the Initial Developer. All Rights Reserved.
021 *
022 * Contributor(s):
023 * See @authors listed below
024 *
025 * Alternatively, the contents of this file may be used under the terms of
026 * either the GNU General Public License Version 2 or later (the "GPL"), or
027 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
028 * in which case the provisions of the GPL or the LGPL are applicable instead
029 * of those above. If you wish to allow use of your version of this file only
030 * under the terms of either the GPL or the LGPL, and not to allow others to
031 * use your version of this file under the terms of the MPL, indicate your
032 * decision by deleting the provisions above and replace them with the notice
033 * and other provisions required by the GPL or the LGPL. If you do not delete
034 * the provisions above, a recipient may use your version of this file under
035 * the terms of any one of the MPL, the GPL or the LGPL.
036 *
037 * ***** END LICENSE BLOCK ***** */
038
039package org.dcm4che3.net;
040
041import java.io.*;
042import java.util.Arrays;
043import java.util.EnumSet;
044import java.util.Set;
045
046import org.dcm4che3.conf.core.api.ConfigurableClass;
047import org.dcm4che3.conf.core.api.ConfigurableProperty;
048import org.dcm4che3.conf.core.api.LDAP;
049import org.dcm4che3.data.UID;
050import org.dcm4che3.util.StringUtils;
051import org.dcm4che3.util.UIDUtils;
052
053/**
054 * DICOM Standard, Part 15, Annex H: Transfer Capability - The description of
055 * the SOP classes and syntaxes supported by a Network AE.
056 * <p>
057 * An instance of the <code>TransferCapability</code> class describes the
058 * DICOM transfer capabilities of an SCU or SCP in terms of a single
059 * presentation syntax. This includes the role selection (SCU or SCP), the
060 * acceptable transfer syntaxes for a given SOP Class, and any extra
061 * information.
062 * 
063 * @author Gunter Zeilinger <gunterze@gmail.com>
064 *
065 */
066@LDAP(objectClasses = {"dicomTransferCapability", "dcmTransferCapability"})
067@ConfigurableClass
068public class TransferCapability implements Serializable {
069
070    private static final long serialVersionUID = 6386251434418693778L;
071
072    public enum Role { SCU, SCP }
073
074    private ApplicationEntity ae;
075
076    @ConfigurableProperty(name="cn")
077    private String commonName;
078
079    @ConfigurableProperty(name="dicomSOPClass")
080    private String sopClass;
081
082    @ConfigurableProperty(name="dicomTransferRole")
083    private Role role;
084
085    @ConfigurableProperty(name="dicomTransferSyntax")
086    private String[] transferSyntaxes;
087
088    @LDAP(  booleanBasedEnumStorageOptions = {"dcmRelationalQueries","dcmCombinedDateTimeMatching","dcmFuzzySemanticMatching","dcmTimezoneQueryAdjustment"},
089            noContainerNode = true)
090    @ConfigurableProperty(name = "dcmQueryOptions", enumRepresentation = ConfigurableProperty.EnumRepresentation.ORDINAL)
091    private EnumSet<QueryOption> queryOptions;
092
093    @LDAP(noContainerNode = true)
094    @ConfigurableProperty(name = "dcmStorageOptions")
095    private StorageOptions storageOptions;
096
097    public TransferCapability() {
098        this(null, UID.VerificationSOPClass, Role.SCU, UID.ImplicitVRLittleEndian);
099    }
100
101    public TransferCapability(String commonName, String sopClass, Role role,
102            String... transferSyntaxes) {
103        setCommonName(commonName);
104        setSopClass(sopClass);
105        setRole(role);
106        setTransferSyntaxes(transferSyntaxes);
107    }
108
109    public void setApplicationEntity(ApplicationEntity ae) {
110        if (ae != null) {
111            if (this.ae != null)
112                throw new IllegalStateException("already owned by AE " + 
113                        this.ae.getAETitle());
114        }
115        this.ae = ae;
116    }
117
118    /**
119     * get the name of the Transfer Capability object. Can be a meaningful name
120     * or any unique sequence of characters.
121     * 
122     * @return A String containing the common name.
123     */
124    public String getCommonName() {
125        return commonName;
126    }
127
128    public void setCommonName(String commonName) {
129        this.commonName = commonName;
130    }
131
132    /**
133     * Get the role for this <code>TransferCapability</code>instance.
134     * 
135     * @return Role (SCU or SCP) for this <code>TransferCapability</code>instance
136     */
137    public Role getRole() {
138        return role;
139    }
140
141    public void setRole(Role role) {
142        if (role == null)
143            throw new NullPointerException();
144
145        if (this.role == role)
146            return;
147
148        ApplicationEntity ae = this.ae;
149        if (ae != null)
150            ae.removeTransferCapabilityFor(sopClass, this.role);
151
152        this.role = role;
153
154        if (ae != null)
155            ae.addTransferCapability(this);
156    }
157
158    /**
159     * Get the SOP Class of this Transfer Capability object.
160     * 
161     * @return A String containing the SOP Class UID.
162     */
163    public String getSopClass() {
164        return sopClass;
165    }
166
167    public void setSopClass(String sopClass) {
168        if (sopClass.isEmpty())
169            throw new IllegalArgumentException("empty sopClass");
170
171        if (sopClass.equals(this.sopClass))
172            return;
173
174        ApplicationEntity ae = this.ae;
175        if (ae != null)
176            ae.removeTransferCapabilityFor(sopClass, this.role);
177
178        this.sopClass = sopClass;
179
180        if (ae != null)
181            ae.addTransferCapability(this);
182    }
183
184    /**
185     * Get the transfer syntax(es) that may be requested as an SCU or that are
186     * offered as an SCP.
187     * 
188     * @return list of transfer syntaxes.
189     */
190    public String[] getTransferSyntaxes() {
191        return transferSyntaxes;
192    }
193
194    public void setTransferSyntaxes(String... transferSyntaxes) {
195        if (transferSyntaxes.length == 0)
196            throw new IllegalArgumentException("missing transferSyntax");
197        for (String ts : transferSyntaxes)
198            if (ts.isEmpty())
199                throw new IllegalArgumentException("empty transferSyntax");
200        this.transferSyntaxes = transferSyntaxes;
201    }
202
203    public boolean containsTransferSyntax(String ts) {
204        if ("*".equals(transferSyntaxes[0]))
205            return true;
206
207        for (String s : transferSyntaxes)
208            if (ts.equals(s))
209                return true;
210
211        return false;
212    }
213
214    public void setQueryOptions(EnumSet<QueryOption> queryOptions) {
215        this.queryOptions = EnumSet.noneOf(QueryOption.class);
216        if (queryOptions != null)
217            this.queryOptions.addAll(queryOptions);
218    }
219
220    public EnumSet<QueryOption> getQueryOptions() {
221        return queryOptions;
222    }
223
224    public void setStorageOptions(StorageOptions storageOptions) {
225        this.storageOptions = storageOptions;
226    }
227
228    public StorageOptions getStorageOptions() {
229        return storageOptions;
230    }
231
232    @Override
233    public String toString() {
234        return promptTo(new StringBuilder(512), "").toString();
235    }
236
237    public TransferCapability deepCopy() {
238        TransferCapability newTc = new TransferCapability();
239
240        newTc.setCommonName(getCommonName());
241        newTc.setQueryOptions(EnumSet.copyOf(getQueryOptions()));
242        newTc.setRole(getRole());
243        newTc.setStorageOptions(getStorageOptions() == null ? null : getStorageOptions().copy());
244        newTc.setSopClass(getSopClass());
245        newTc.setTransferSyntaxes(Arrays.copyOf(getTransferSyntaxes(),getTransferSyntaxes().length));
246
247        return newTc;
248    }
249
250    public StringBuilder promptTo(StringBuilder sb, String indent) {
251        String indent2 = indent + "  ";
252        StringUtils.appendLine(sb, indent, "TransferCapability[cn: ", commonName);
253        StringUtils.appendLine(sb, indent2, "role: ", role);
254        sb.append(indent2).append("as: ");
255        UIDUtils.promptTo(sopClass, sb).append(StringUtils.LINE_SEPARATOR);
256        for (String ts : transferSyntaxes) {
257            sb.append(indent2).append("ts: ");
258            UIDUtils.promptTo(ts, sb).append(StringUtils.LINE_SEPARATOR);
259        }
260        if (queryOptions != null)
261            sb.append(indent2).append("QueryOptions").append(queryOptions)
262                .append(StringUtils.LINE_SEPARATOR);
263        if (storageOptions != null)
264            sb.append(indent2).append(storageOptions)
265                .append(StringUtils.LINE_SEPARATOR);
266        return sb.append(indent).append(']');
267    }
268
269}