Project

General

Profile

Revision b93248b0

IDb93248b0386ab071212fa3f737942a7bbb5266bb
Parent 5de5776a
Child 98ad8fb7

Added by Andreas Müller about 3 years ago

ref #6636 first running version of RisReferenceImport, some important tags are still missing

View differences:

cdmlib-io/src/main/java/eu/etaxonomy/cdm/io/common/ImportResult.java
100 100
    public Map<String, Integer> getNewRecords() {
101 101
        return clone(newRecords);
102 102
    }
103
    public Integer getNewRecords(Class<? extends CdmBase> clazz) {
104
        return clone(newRecords).get(clazz.getSimpleName());
105
    }
103 106

  
104 107
    public Map<String, Integer> getUpdatedRecords() {
105 108
        return clone(updatedRecords);
......
121 124
        return clone(deletedRecords);
122 125
    }
123 126

  
127
    //new records
124 128
    public void addNewRecord(String clazz){
125 129
        addNewRecords(clazz, 1);
126 130
    }
127 131
    public void addNewRecords(String clazz, int count){
128 132
        addRecord(newRecords, clazz, count);
129 133
    }
134
    public void addNewRecords(Class<? extends CdmBase> clazz, int count){
135
        addRecord(newRecords, clazz.getSimpleName(), count);
136
    }
137
    public void addNewRecord(CdmBase newRecord) {
138
        this.addNewRecord(CdmBase.deproxy(newRecord).getClass().getSimpleName());
139
    }
140

  
141

  
142
    //updated records
130 143
    public void addUpdatedRecord(String clazz){
131 144
        addUpdatedRecords(clazz, 1);
132 145
    }
133 146
    public void addUpdatedRecords(String clazz, int count){
134 147
        addRecord(updatedRecords, clazz, count);
135 148
    }
149
    public void addUpdatedRecord(CdmBase updatedRecord) {
150
        this.addUpdatedRecord(CdmBase.deproxy(updatedRecord).getClass().getSimpleName());
151
    }
152

  
153
    //deleted
136 154
    public void addDeletedRecord(String clazz){
137 155
        addDeletedRecords(clazz, 1);
138 156
    }
......
151 169
        }
152 170
    }
153 171

  
154
    public void addNewRecord(CdmBase newRecord) {
155
        this.addNewRecord(CdmBase.deproxy(newRecord).getClass().getSimpleName());
156
    }
157
    public void addUpdatedRecord(CdmBase updatedRecord) {
158
        this.addUpdatedRecord(CdmBase.deproxy(updatedRecord).getClass().getSimpleName());
159
    }
172

  
160 173
    public void addDeletedRecord(CdmBase deletedRecord) {
161 174
        this.addDeletedRecord(CdmBase.deproxy(deletedRecord).getClass().getSimpleName());
162 175
    }
cdmlib-io/src/main/java/eu/etaxonomy/cdm/io/common/IoResultBase.java
20 20
 */
21 21
public abstract class IoResultBase {
22 22

  
23
    private List<IoInfo> ioInfos = new ArrayList<>();
23
    private List<IoInfo> errors = new ArrayList<>();
24 24
    private List<IoInfo> warnings = new ArrayList<>();
25 25
    private List<IoInfo> exceptions = new ArrayList<>();
26 26

  
......
49 49

  
50 50
// ************* GETTERS / SETTERS / ADDERS ***********************/
51 51

  
52
    public List<IoInfo> getErrors() {return ioInfos;}
53
    public void setErrors(List<IoInfo> ioInfos) {this.ioInfos = ioInfos;}
52
    public List<IoInfo> getErrors() {return errors;}
53
    public void setErrors(List<IoInfo> ioInfos) {this.errors = ioInfos;}
54 54
    public void addError(String error) {
55
        ioInfos.add(new IoInfo(error, null));
55
        errors.add(new IoInfo(error, null));
56 56
    }
57 57
    public void addError(String error, Exception e) {
58
        ioInfos.add(new IoInfo(error, e));
58
        errors.add(new IoInfo(error, e));
59 59
    }
60 60
    public void addError(String message, int location) {
61
        ioInfos.add(new IoInfo(message, null, String.valueOf(location)));
61
        errors.add(new IoInfo(message, null, String.valueOf(location)));
62 62
    }
63 63

  
64 64
    public List<IoInfo> getWarnings() {return warnings;}
......
70 70
    public void addWarning(String message, int location) {
71 71
        warnings.add(new IoInfo(message, null, String.valueOf(location)));
72 72
    }
73
    public void addWarning(String message, String location) {
74
        warnings.add(new IoInfo(message, null, location));
75
    }
73 76

  
74 77
    public List<IoInfo> getExceptions() {return exceptions;}
75 78
    public void setExceptions(List<IoInfo> exceptions) {this.exceptions = exceptions;}
......
100 103
     */
101 104
    public StringBuffer createReport() {
102 105
        StringBuffer report = new StringBuffer("");
103
        addErrorReport(report, "Errors", ioInfos);
106
        addErrorReport(report, "Errors", errors);
104 107
        addErrorReport(report, "Exceptions", exceptions);
105 108
        addErrorReport(report, "Warnings", warnings);
106 109
        return report;
......
126 129
     * @param list
127 130
     */
128 131
    private void addErrorReport(StringBuffer report, String label, List<IoInfo> list) {
129
        if (!ioInfos.isEmpty()){
132
        if (!list.isEmpty()){
130 133
            report.append("\n\n" + label + ":\n" + StringUtils.leftPad("", label.length()+1, "="));
131 134
            for (IoInfo ioInfo : list){
132 135
                String location = ioInfo.location == null ? "" : (ioInfo.location + ": ");
133 136
                String message = ioInfo.message != null ? ioInfo.message : ioInfo.exception != null ? ioInfo.exception.getMessage() : "";
134 137
                message = StringUtils.isBlank(message)? "no message" : message;
135
                Object stacktrace = ioInfo.exception.getStackTrace();
138
                Object stacktrace = ioInfo.exception == null? null : ioInfo.exception.getStackTrace();
136 139
                String available = (stacktrace == null ? " not" : "");
137
                report.append("\n" + location + message + "(stacktrace" + available + ")");
140
                report.append("\n" + location + message + "(stacktrace" + available + " available)");
138 141
            }
139 142
        }
140 143
    }
cdmlib-io/src/main/java/eu/etaxonomy/cdm/io/reference/ris/in/RisRecordReader.java
11 11
import java.io.BufferedReader;
12 12
import java.io.IOException;
13 13
import java.io.InputStreamReader;
14
import java.util.ArrayList;
14 15
import java.util.HashMap;
16
import java.util.List;
15 17
import java.util.Map;
16 18

  
17 19
import org.apache.commons.lang3.StringUtils;
......
28 30
    private int lineNo = 0;
29 31
    private String lineNoStr;
30 32

  
31
    public static final Map<RisReferenceTag, String>  EOF = new HashMap<>();
33
    public static final Map<RisReferenceTag, List<RisValue>>  EOF = new HashMap<>();
32 34
    private static final String NL = "\n";
33 35

  
36
    protected class RisValue{
37
        RisReferenceTag tag;
38
        String value;
39
        String location;
40
        public RisValue(String value, String location, RisReferenceTag tag) {
41
            this.value = value; this.location = "line " + location; this.tag = tag;
42
        }
43
        @Override
44
        public String toString() {
45
            return location + ": " + tag + ": " + value;
46
        }
47
    }
48

  
34 49

  
35 50
    /**
36 51
     * @param baseReader
......
40 55
        this.state = state;
41 56
    }
42 57

  
43
    public Map<RisReferenceTag, String> readRecord(){
58
    public Map<RisReferenceTag, List<RisValue>> readRecord(){
44 59
        try {
45
            Map<RisReferenceTag, String> result = new HashMap<>();
60
            Map<RisReferenceTag, List<RisValue>> result = new HashMap<>();
46 61
            String line;
47 62
            int count = 0;
48
            boolean started = false;
49 63
            RisReferenceTag lastType = null;
50 64
            while ((line = lineReader.readLine()) != null) {
51 65
                lineNo++;
......
53 67
                   continue;
54 68
               }
55 69
               RisReferenceTag type;
70
               if (Integer.valueOf(line.toCharArray()[0]).equals(65279)  ){ //remove BOM cotrol character if encoding is not correctly working
71
                   line = line.substring(1);
72
               }
73

  
56 74
               if (matchesRisLine(line)){
57 75
                   type = RisReferenceTag.TY;
58 76
                   if (isTypeLine(line)){
59 77
                       type = RisReferenceTag.TY;
60
                       result.put(type, replaceTag(line));
78
                       addTaggedValue(type, result, line, lineNo);
61 79
                       count++;
62 80
                   }else if (isErLine(line)){
63 81
                       return result;
......
74 92
                           String message = "Unknown reference type %s . Reference attribute could not be added";
75 93
                           state.getResult().addWarning(message, lineNo);
76 94
                       }else{
77
                           result.put(type, replaceTag(line));
95
                           addTaggedValue(type, result, line, lineNo);
78 96
                       }
79 97
                       count++;
80 98
                   }
......
82 100
                       lastType = type;
83 101
                   }
84 102
               }else{
85
                   if (started){
103
                   if (result.size() > 0){
86 104
                       //add to prior
87
                       String prior = result.get(lastType);
88
                       result.put(lastType, prior + NL + line);
105
                       List<RisValue> priorList = result.get(lastType);
106
                       RisValue priorValue = priorList.get(priorList.size()-1);
107
                       priorValue.value = priorValue + NL + line;
89 108
                   }else{
90
                       String message = lineNoStr + "RIS record does not start with TY. Can't create record";
109
                       String message = "RIS record does not start with TY. Can't create record";
91 110
                       state.getResult().addError(message, lineNo);
92 111
                   }
93 112

  
......
108 127

  
109 128

  
110 129
    /**
130
     * @param result
111 131
     * @param line
132
     * @param lineNo2
112 133
     * @return
113 134
     */
114
    private String replaceTag(String line) {
115
        return line.substring(5).trim();
135
    private void addTaggedValue(RisReferenceTag tag, Map<RisReferenceTag, List<RisValue>> result, String line, int lineNo) {
136
        String value = replaceTag(line);
137
        List<RisValue> list = result.get(tag);
138
        if (list == null){
139
            list = new ArrayList<>();
140
            result.put(tag, list);
141
        }
142
        RisValue risValue = new RisValue(value, "" + lineNo, tag);
143
        list.add(risValue);
144
        return;
116 145
    }
117 146

  
118

  
119
    private static final String risLineReStr = "[A-Z1-9]{2}\\s\\s-\\s.*";
120
    private static final String typeLineReStr = "TY\\s\\s-\\s.*";
121
    private static final String erLineReStr = "ER\\s\\s-\\s+";
122

  
123

  
124 147
    /**
125 148
     * @param line
126 149
     * @return
127 150
     */
151
    private String replaceTag(String line) {
152
        return line.substring(5).trim();
153
    }
154

  
155

  
156
    private static final String erLineReStr = "ER\\s\\s-\\s+";
128 157
    private boolean isErLine(String line) {
129 158
        return line.matches(erLineReStr);
130 159
    }
131 160

  
132 161

  
133
    /**
134
     * @param line
135
     * @return
136
     */
162
    private static final String typeLineReStr = "TY\\s\\s-\\s.*";
137 163
    private boolean isTypeLine(String line) {
138 164
        return line.matches(typeLineReStr);
139 165
    }
140 166

  
141

  
142
    /**
143
     * @param line
144
     * @return
145
     */
167
    private static final String risLineReStr = "[A-Z1-9]{2}\\s\\s-\\s.*";
146 168
    private boolean matchesRisLine(String line) {
147
        return line.matches(risLineReStr);
169
        boolean matches = line.matches(risLineReStr);
170
        return matches;
148 171
    }
149 172

  
150 173
    private boolean isBlank(String str){
cdmlib-io/src/main/java/eu/etaxonomy/cdm/io/reference/ris/in/RisReferenceImport.java
9 9
package eu.etaxonomy.cdm.io.reference.ris.in;
10 10

  
11 11
import java.io.InputStreamReader;
12
import java.util.ArrayList;
13
import java.util.Arrays;
12 14
import java.util.HashSet;
15
import java.util.List;
13 16
import java.util.Map;
14 17
import java.util.Set;
15 18

  
16 19
import org.apache.log4j.Logger;
17 20
import org.springframework.stereotype.Component;
18 21

  
22
import eu.etaxonomy.cdm.common.CdmUtils;
19 23
import eu.etaxonomy.cdm.common.DOI;
20 24
import eu.etaxonomy.cdm.io.common.CdmImportBase;
25
import eu.etaxonomy.cdm.io.reference.ris.in.RisRecordReader.RisValue;
21 26
import eu.etaxonomy.cdm.model.agent.Person;
27
import eu.etaxonomy.cdm.model.agent.Team;
28
import eu.etaxonomy.cdm.model.agent.TeamOrPersonBase;
22 29
import eu.etaxonomy.cdm.model.common.Annotation;
23 30
import eu.etaxonomy.cdm.model.common.AnnotationType;
24 31
import eu.etaxonomy.cdm.model.common.Language;
......
26 33
import eu.etaxonomy.cdm.model.reference.Reference;
27 34
import eu.etaxonomy.cdm.model.reference.ReferenceFactory;
28 35
import eu.etaxonomy.cdm.model.reference.ReferenceType;
29
import eu.etaxonomy.cdm.strategy.parser.TimePeriodParser;
30 36

  
31 37
/**
32 38
 * @author a.mueller
......
55 61
            Set<Reference> referencesToSave = new HashSet<>();
56 62

  
57 63

  
58
            Map<RisReferenceTag, String> next = risReader.readRecord();
64
            Map<RisReferenceTag, List<RisValue>> next = risReader.readRecord();
59 65
            while (next != RisRecordReader.EOF){
60
                Reference ref = makeReference(next);
61
                referencesToSave.add(ref);
66
                Reference ref;
67
                try {
68
                    ref = makeReference(state, next);
69
                    referencesToSave.add(ref);
70
                    if (ref.getInReference() != null){
71
                        referencesToSave.add(ref.getInReference());
72
                    }
73
                } catch (Exception e) {
74
                    String message = "Unexpected exception during RIS Reference Import";
75
                    state.getResult().addException(e, message);
76
                }
77

  
62 78
                next = risReader.readRecord();
63 79
            }
64 80

  
65 81
            getReferenceService().saveOrUpdate(referencesToSave);
82
            state.getResult().addNewRecords(Reference.class.getSimpleName(), referencesToSave.size());
66 83

  
67 84
        } catch (Exception e) {
68 85
            String message = "Unexpected exception during RIS Reference Import";
......
71 88
    }
72 89

  
73 90
    /**
91
     * @param state
74 92
     * @param next
75 93
     * @return
76 94
     */
77
    private Reference makeReference(Map<RisReferenceTag, String> record) {
78
        ReferenceType type = makeReferenceType(record);
79
        Reference result = ReferenceFactory.newReference(type);
80
        ReferenceType inRefType = type == ReferenceType.Article ? ReferenceType.Journal : ReferenceType.Generic;
81
        Reference inRef = ReferenceFactory.newReference(inRefType);
95
    private Reference makeReference(RisReferenceImportState state,
96
            Map<RisReferenceTag, List<RisValue>> record) {
97

  
98
        //type
99
        ReferenceType type = makeReferenceType(state, record);
100
        Reference ref = ReferenceFactory.newReference(type);
101
        Reference inRef = null;
102
        if (ref.getType().isSection()){
103
            ReferenceType inRefType =
104
                    type == ReferenceType.Article ? ReferenceType.Journal:
105
                    type == ReferenceType.BookSection ? ReferenceType.Book :
106
                        ReferenceType.Generic;
107
            inRef = ReferenceFactory.newReference(inRefType);
108
            ref.setInReference(inRef);
109
        }
110
        Reference higherRef = inRef == null ? ref : inRef;
111

  
112

  
113
        //Title
114
        RisValue t1 = getSingleValue(state, record, RisReferenceTag.T1);
115
        RisValue ti = getSingleValue(state, record, RisReferenceTag.TI);
116
        RisValue value = assertEqual(state, "title", t1, ti);
117
        if (value != null){
118
            ref.setTitle(value.value);
119
        }
120

  
121
        //Journal title
122
        RisValue t2 = getSingleValue(state, record, RisReferenceTag.T2); //Secondary Title (journal title, if applicable)
123

  
124
        if (higherRef.getType() == ReferenceType.Journal){
125
            RisValue jf = getSingleValue(state, record, RisReferenceTag.JF); //Journal/Periodical name: full format. This is an alphanumeric field of up to 255 characters.
126
            RisValue jo = getSingleValue(state, record, RisReferenceTag.JO); //Journal/Periodical name: full format. This is an alphanumeric field of up to 255 characters.
127
            RisValue x = assertEqual(state, "Journal/Periodical name: full format", jf, jo);
128
            x = assertEqual(state, "Journal title", t2, x);
129
            if (x != null){
130
                higherRef.setTitle(x.value);
131
            }
132
        }else{
133
            //TODO
134
        }
135

  
136

  
137
        //Author
138
        List<RisValue> list = getListValue(record, RisReferenceTag.AU);
139
        if (!list.isEmpty()){
140
            TeamOrPersonBase<?> author = makeAuthor(state, list);
141
            ref.setAuthorship(author);
142
        }
143

  
144
        //Date
145
//        RisValue y1 = getSingleValue(state, record, RisReferenceTag.Y1); //Primary Date
146
        RisValue py = getSingleValue(state, record, RisReferenceTag.PY);
147
        RisValue da = getSingleValue(state, record, RisReferenceTag.DA);
148
        Integer year = makeYear(state, py);
149
        TimePeriod date = makeDate(state, da);
150
        assertDateYear(state, year, date, py);
151
        ref.setDatePublished(date);
152
        //TODO y1 not yet handled
153

  
154
        //Note
155
        RisValue n1 = getSingleValue(state, record, RisReferenceTag.N1); //Note
156
        if (n1 != null){
157
            Annotation annotation = Annotation.NewInstance(n1.value, AnnotationType.EDITORIAL(), Language.DEFAULT());
158
            ref.addAnnotation(annotation);
159
        }
160

  
161
        //DOI
162
        RisValue doiVal = getSingleValue(state, record, RisReferenceTag.DO); //Doi
163
        if (doiVal != null){
164
            DOI doi;
165
            try {
166
                doi = DOI.fromString(doiVal.value);
167
                ref.setDoi(doi);
168
            } catch (IllegalArgumentException e) {
169
                String message = "DOI could not be recognized: " + doiVal.value;
170
                state.getResult().addWarning(message, doiVal.location);
171
            }
172
        }
173

  
174
        //Pages
175
        RisValue sp = getSingleValue(state, record, RisReferenceTag.SP);
176
        RisValue ep = getSingleValue(state, record, RisReferenceTag.EP);
177
        String pages = CdmUtils.concat("-", sp != null ? sp.value : null, ep != null ? ep.value : null);
178
        ref.setPages(pages);
179

  
180
        //Volume
181
        RisValue vl = getSingleValue(state, record, RisReferenceTag.VL);
182
        RisValue is = getSingleValue(state, record, RisReferenceTag.IS);
183
        String vol = vl == null? "": vl.value + (is != null ? "("+ is.value + ")": "");
184
        ref.setVolume(vol);
185

  
186
        //Publisher
187
        RisValue pb = getSingleValue(state, record, RisReferenceTag.PB);
188
        if (pb != null){
189
            higherRef.setPublisher(pb.value);
190
        }
191

  
192
        //Abstract
193
        RisValue ab = getSingleValue(state, record, RisReferenceTag.AB);
194
        RisValue n2 = getSingleValue(state, record, RisReferenceTag.N2);
195
        RisValue abst = assertEqual(state, "Abstract", ab, n2);
196
        if (abst != null){
197
            ref.setReferenceAbstract(abst.value);
198
        }
199

  
200
        //ISSN/ISBN
201
        RisValue sn = getSingleValue(state, record, RisReferenceTag.SN);
202
        if (sn != null){
203
            if (higherRef.getType() == ReferenceType.Journal){
204
                higherRef.setIssn(sn.value);
205
            }else{
206
                higherRef.setIsbn(sn.value);
207
            }
208
        }
209

  
210
        //
82 211

  
83 212
        for (RisReferenceTag tag : record.keySet()){
84
            String value = record.get(tag);
85

  
86
            if (isNotBlank(value)){
87
                switch (tag) {
88
                    case T1:
89
                        result.setTitle(value);
90
                        break;
91
                    case AU:
92
                        Person author = Person.NewTitledInstance(value);
93
                        result.setAuthorship(author);
94
                        break;
95
                    case Y1:
96
                        TimePeriod y1 = TimePeriodParser.parseString(value);
97
                        result.setDatePublished(y1);
98
                        break;
99
                    case PY:
100
                        //TODO
101
                        TimePeriod py = TimePeriodParser.parseString(value);
102
                        result.setDatePublished(py);
103
                        break;
104
                    case DA:
105
                        //TODO
106
                        TimePeriod da = TimePeriodParser.parseString(value);
107
                        result.setDatePublished(da);
108
                        break;
109
                    case N1:
110
                        Annotation annotation = Annotation.NewInstance(value, AnnotationType.EDITORIAL(), Language.DEFAULT());
111
                        result.addAnnotation(annotation);
112
                        break;
113
                    case DO:
114
                        DOI doi = DOI.fromString(value);
115
                        result.setDoi(doi);
116
                        break;
117
                    case T2:
118
                        inRef.setTitle(value);
119
                        break;
120
                    case JF:
121
                        inRef.setTitle(value);
122
                        break;
123
                    case JO:
124
                        inRef.setTitle(value);
125
                        break;
126
                    case SP:
127
                        String startPage = value;
128
                        result.setPages(startPage);
129
                        break;
130
                    case EP:
131
                        String endPage = value;
132
                        result.setPages(endPage);
133
                        break;
134
                    case VL:
135
                        String volume = value;
136
                        result.setVolume(volume);
137
                        break;
138
                    case IS:
139
                        String issueNumber = value;
140
                        result.setVolume(issueNumber);
141
                        break;
142
                    case PB:
143
                        String publisher = value;
144
                        result.setPublisher(publisher);
145
                        break;
146
                    case N2:
147
                        String n2Str = value;
148
                        result.setReferenceAbstract(n2Str);
149
                        break;
150
                    case AB:
151
                        String abstractStr = value;
152
                        result.setReferenceAbstract(abstractStr);
153
                        break;
154
                    case SN:
155
                        String issn = value;
156
                        result.setIssn(issn);
157
                        break;
158
                    default:
159
                        //TODO
160
                        break;
161
                }
162
            }else {
163
                //TODO isBlank
213
            //TODO we may want to count them for the whole file and not mention each time when not available
214
            String message = "RIS Tag " + tag.name() +  " not yet handled";
215
            state.getResult().addWarning(message, record.get(tag).get(0).location);
216

  
217
            //TODO add as annotation or extension
218
        }
219

  
220
        return ref;
221
    }
222

  
223

  
224
    /**
225
     * @param state
226
     * @param year
227
     * @param date
228
     */
229
    private void assertDateYear(RisReferenceImportState state, Integer year, TimePeriod date, RisValue py) {
230
        if (year != null && date != null && !year.equals(date.getStartYear())){
231
            String message = "Year 'PY' and date 'DA' are not consistent. PY is neglected.";
232
            state.getResult().addWarning(message, py.location);
233
        }
234
    }
235

  
236
    private RisValue assertEqual(RisReferenceImportState state, String meaning, RisValue val1, RisValue val2) {
237
        if (val1 != null && val2 != null && !val1.value.equals(val2.value)){
238
            String message = "The tags '%s' and '%s' are not equal but have a similar meaning ('%s'). "
239
                    + "%s was used and %s neglected";
240
            message = String.format(message, val1.tag.name(), val2.tag.name(), meaning , val1.tag.name(), val2.tag.name());
241
            state.getResult().addWarning(message, val1.location);
242
        }
243
        return val1 != null ? val1 : val2;
244
    }
245

  
246
    /**
247
     * @param state
248
     * @param da
249
     * @return
250
     */
251
    private TimePeriod makeDate(RisReferenceImportState state, RisValue da) {
252
        if (da == null){
253
            return null;
254
        }
255
        if (! da.value.matches("([0-9]{4})?(\\/([0-9]{2})?(\\/([0-9]{2})?(\\/.*)?)?)?")){
256
            String message = "Tag '%s' has incorrect format. Only exactly 'dddd/dd/dd/any text' is allowed (where d is a digit), but was '%s'";
257
            message = String.format(message, da.tag.name(), da.value);
258
            state.getResult().addWarning(message, da.location);
259
            return null;
260
        }
261
        String[] split = da.value.split("/");
262
        TimePeriod tp = TimePeriod.NewInstance();
263
        if (split.length > 0 && isNotBlank(split[0])){
264
            tp.setStartYear(Integer.valueOf(split[0]));
265
        }
266
        if (split.length > 1 && isNotBlank(split[1])){
267
            tp.setStartMonth(Integer.valueOf(split[1]));
268
        }
269
        if (split.length > 2 && isNotBlank(split[2])){
270
            tp.setStartDay(Integer.valueOf(split[2]));
271
        }
272
        if (split.length > 3 && isNotBlank(split[3])){
273
            List<String> other = Arrays.asList(split).subList(3, split.length);
274
            String otherStr = CdmUtils.concat("/", other.toArray(new String[other.size()]));
275
            tp.setFreeText(tp.toString() + " " + otherStr);
276
        }
277
        return tp;
278
    }
279

  
280
    /**
281
     * @param state
282
     * @param py
283
     * @return
284
     */
285
    private Integer makeYear(RisReferenceImportState state, RisValue py) {
286
        if (py == null){
287
            return null;
288
        }
289
        if (py.value.matches("[0-9]{4}")){
290
            return Integer.valueOf(py.value);
291
        }else{
292
            String message = "Tag '%s' has incorrect format. Only exactly 4 digits are allowed, but was '%s'";
293
            message = String.format(message, py.tag.name(), py.value);
294
            state.getResult().addWarning(message, py.location);
295
            return null;
296
        }
297
    }
298

  
299
    /**
300
     * @param state
301
     * @param list
302
     * @return
303
     */
304
    private TeamOrPersonBase<?> makeAuthor(RisReferenceImportState state, List<RisValue> list) {
305
        if (list.size() == 1){
306
            return makePerson(state, list.get(0));
307
        }else{
308
            Team team = Team.NewInstance();
309
            for (RisValue value : list){
310
                team.addTeamMember(makePerson(state, value));
164 311
            }
312
            return team;
313
        }
314
    }
315

  
316
    /**
317
     * @param state
318
     * @param risValue
319
     * @return
320
     */
321
    private Person makePerson(RisReferenceImportState state, RisValue risValue) {
322
        Person person = Person.NewInstance();
323
        String[] split = risValue.value.split(",");
324
        if (split.length >= 1){
325
            person.setLastname(split[0].trim());
326
        }
327
        if (split.length >= 2){
328
            person.setFirstname(split[1].trim());
329
        }
330
        if (split.length >= 3){
331
            person.setSuffix(split[2].trim());
332
        }
333

  
334
        return person;
335
    }
165 336

  
337
    /**
338
     * Returns the single value for the given tag
339
     * and removes the tag from the record.
340
     * If more than 1 value exists this is logged
341
     * as a warning.
342
     */
343
    private RisValue getSingleValue(RisReferenceImportState state,
344
            Map<RisReferenceTag, List<RisValue>> record,
345
            RisReferenceTag tag) {
346
        List<RisValue> list = record.get(tag);
347
        if (list == null){
348
            return null;
349
        }
350
        assertSingle(state, list, tag);
351
        record.remove(tag);
352
        return list.get(0);
353
    }
354

  
355
    private List<RisValue> getListValue(Map<RisReferenceTag, List<RisValue>> record,
356
            RisReferenceTag tag) {
357
        List<RisValue> list = record.get(tag);
358
        record.remove(tag);
359
        if (list == null){
360
            list = new ArrayList<>();
166 361
        }
167
        return result;
362
        return list;
168 363
    }
169 364

  
170 365
    /**
171
     * @param record
366
     * @param state
367
     * @param list
368
     * @param tag
369
     */
370
    private void assertSingle(RisReferenceImportState state, List<RisValue> list, RisReferenceTag tag) {
371
        if (list.size() > 1){
372
            String message = "There is more than 1 tag '%s' but only 1 tag is supported by RIS format or"
373
                    + " by the current import implementation.";
374
            message = String.format(message, tag.name());
375
            state.getResult().addWarning(message, list.get(0).location + "ff");
376
        }else if (list.isEmpty()){
377
            state.getResult().addError("A tag list was empty. This should not happen and is a programming code error");
378
        }
379
    }
380

  
381
    /**
382
     * @param state
383
     * @param next
172 384
     * @return
173 385
     */
174
    private ReferenceType makeReferenceType(Map<RisReferenceTag, String> record) {
386
    private ReferenceType makeReferenceType(RisReferenceImportState state,
387
            Map<RisReferenceTag, List<RisValue>> record) {
175 388
        RisReferenceTag tyTag = RisReferenceTag.TY;
176
        String typeStr = record.get(tyTag);
389
        RisValue value = this.getSingleValue(state, record, tyTag);
390
        String typeStr = value.value;
177 391
        RisRecordType type = RisRecordType.valueOf(typeStr);
178 392
        ReferenceType cdmType = type.getCdmReferenceType();
179
        record.remove(tyTag);
180 393
        return cdmType;
181 394
    }
182 395

  
cdmlib-io/src/main/java/eu/etaxonomy/cdm/io/reference/ris/in/RisReferenceImportConfigurator.java
52 52
     */
53 53
    public static IImportConfigurator NewInstance(URL url, ICdmDataSource cdm) throws IOException {
54 54
        InputStream stream = url.openStream();
55
        InputStreamReader reader = new InputStreamReader(stream);
55
        InputStreamReader reader = new InputStreamReader(stream, "UTF8");
56 56
        RisReferenceImportConfigurator result = new RisReferenceImportConfigurator(reader, cdm);
57 57
        return result;
58 58
    }
cdmlib-io/src/test/java/eu/etaxonomy/cdm/io/reference/RisReferenceImportTest.java
13 13

  
14 14
import java.io.FileNotFoundException;
15 15
import java.net.URL;
16
import java.util.List;
16 17

  
17 18
import org.junit.Assert;
18 19
import org.junit.Before;
19
import org.junit.Ignore;
20 20
import org.junit.Test;
21 21
import org.unitils.spring.annotation.SpringBeanByName;
22 22
import org.unitils.spring.annotation.SpringBeanByType;
23 23

  
24
import eu.etaxonomy.cdm.api.service.INameService;
24
import eu.etaxonomy.cdm.api.service.IReferenceService;
25
import eu.etaxonomy.cdm.common.DOI;
25 26
import eu.etaxonomy.cdm.io.common.CdmApplicationAwareDefaultImport;
26 27
import eu.etaxonomy.cdm.io.common.IImportConfigurator;
27 28
import eu.etaxonomy.cdm.io.common.ImportResult;
28 29
import eu.etaxonomy.cdm.io.reference.ris.in.RisReferenceImportConfigurator;
30
import eu.etaxonomy.cdm.model.agent.Person;
31
import eu.etaxonomy.cdm.model.agent.TeamOrPersonBase;
32
import eu.etaxonomy.cdm.model.common.CdmBase;
33
import eu.etaxonomy.cdm.model.common.TimePeriod;
34
import eu.etaxonomy.cdm.model.reference.Reference;
35
import eu.etaxonomy.cdm.model.reference.ReferenceType;
29 36
import eu.etaxonomy.cdm.test.integration.CdmTransactionalIntegrationTest;
30 37

  
31 38
/**
......
38 45
	private CdmApplicationAwareDefaultImport<?> defaultImport;
39 46

  
40 47
	@SpringBeanByType
41
	private INameService nameService;
48
	private IReferenceService referenceService;
42 49

  
43 50
	private IImportConfigurator configurator;
51
    private IImportConfigurator configLong;
44 52

  
45 53
	@Before
46 54
	public void setUp() {
47 55
		String inputFile = "/eu/etaxonomy/cdm/io/reference/RisReferenceImportTest-input.ris";
48 56
		URL url = this.getClass().getResource(inputFile);
49 57
		assertNotNull("URL for the test file '" + inputFile + "' does not exist", url);
58

  
59
		String inputFileLong = "/eu/etaxonomy/cdm/io/reference/Acantholimon.ris";
60
        URL urlLong = this.getClass().getResource(inputFileLong);
61
        assertNotNull("URL for the test file '" + inputFileLong + "' does not exist", urlLong);
62

  
50 63
		try {
51 64
			configurator = RisReferenceImportConfigurator.NewInstance(url, null);
65
			configLong = RisReferenceImportConfigurator.NewInstance(urlLong, null);
66

  
52 67
		} catch (Exception e) {
53 68
			e.printStackTrace();
54 69
			Assert.fail();
55 70
		}
56 71
		assertNotNull("Configurator could not be created", configurator);
72
	    assertNotNull("Configurator could not be created", configLong);
73
	    assertNotNull("nameService should not be null", referenceService);
57 74
	}
58 75

  
59 76
//***************************** TESTS *************************************//
60 77

  
61 78
	@Test
62
	public void testInit() {
63
//		assertNotNull("XXX should not be null", defaultImport);
64
		assertNotNull("nameService should not be null", nameService);
65
		assertNotNull("configurator should not be null", configurator);
66
	}
67

  
68
	@Test
69
	public void testDoInvokeWithoutExceptions() {
79
	public void testShort() {
70 80
		ImportResult result = defaultImport.invoke(configurator);
71 81
		String report = result.createReport().toString();
72
		System.out.println(report);
82
		Assert.assertTrue(report.length() > 0);
83
//		System.out.println(report);
84

  
85
		Integer expected = 2;
86
		Assert.assertEquals(expected, result.getNewRecords(Reference.class));
87

  
88
		List<Reference> list = referenceService.list(Reference.class, null, null, null, null);
89
		Assert.assertEquals("There should be 2 references, the article and the journal", 2, list.size());
90
		for (Reference ref : list){
91
		    Assert.assertTrue(ref.getType() == ReferenceType.Article || ref.getType() == ReferenceType.Journal);
92
		    if (ref.getType() == ReferenceType.Article){
93
		        //title
94
		        Assert.assertEquals("Decorsella arborea, a second species in Decorsella (Violaceae), and Decorsella versus Rinorea",
95
		                ref.getTitle());
96
		        //author
97
		        TeamOrPersonBase<?> author = ref.getAuthorship();
98
		        Assert.assertNotNull(author);
99
		        Assert.assertTrue(author.isInstanceOf(Person.class));
100
		        Person person = CdmBase.deproxy(author, Person.class);
101
		        //this may change in future depending on the correct formatting strategy
102
		        Assert.assertEquals("Carel C. H. Jongkind" ,person.getTitleCache());
103
		        Assert.assertEquals("Jongkind" ,person.getLastname());
104
		        Assert.assertEquals("Carel C. H." ,person.getFirstname());
105
		        //date
106
		        TimePeriod date = ref.getDatePublished();
107
		        Assert.assertEquals(Integer.valueOf(2017) ,date.getStartYear());
108
		        //vol
109
		        Assert.assertEquals("47(1)" ,ref.getVolume());
110
                Assert.assertEquals("43-47" ,ref.getPages());
111

  
112
                //doi
113
                Assert.assertEquals(DOI.fromString("10.3372/wi.47.47105"),ref.getDoi());
114

  
115
                //Abstract
116
                Assert.assertEquals("Abstract: A new species of Violaceae, Decorsella arborea Jongkind, is described and illustrated. The new species differs from the only other species in the genus, D. paradoxa A. Chev., by the larger size of the plants, smaller leaves, more slender flowers, and stamen filaments that are free for a much larger part. Both species are from the Guineo-Congolian forest of tropical Africa. The differences between Decorsella and Rinorea are discussed. Confirming recent reports, some species of Rinorea can have zygomorphic flowers and some of these can be almost equal in shape to Decorsella flowers. Citation: Jongkind C. C. H. 2017: Decorsella arborea, a second species in Decorsella (Violaceae), and Decorsella versus Rinorea. ? Willdenowia 47: 43?47. doi: https://doi.org/10.3372/wi.47.47105 Version of record first published online on 13 February 2017 ahead of inclusion in April 2017 issue.",
117
                        ref.getReferenceAbstract());
118

  
119
                //TODO still missing Y1, Y2, M3, UR
120

  
121
		    }else if (ref.getType() == ReferenceType.Journal){
122
		        Assert.assertEquals("Willdenowia", ref.getTitle());
123
		        //or is this part of article?
124
		        Assert.assertEquals("Botanic Garden and Botanical Museum Berlin (BGBM)", ref.getPublisher());
125

  
126
		        //ISSN
127
                Assert.assertEquals("0511-9618" ,ref.getIssn());
128

  
129
		    }else{
130
		        Assert.fail("Only an article and a journal should exist");
131
		    }
132
		}
133

  
73 134
	}
74 135

  
75 136
	@Test
76
	@Ignore("Import does not fully work yet")
77
	public void testDoInvoke() {
78
		boolean result = defaultImport.invoke(configurator).isSuccess();
79
		//TODO result is still false
80
		logger.warn("No real testing for ris import yet");
81
		Assert.assertTrue("Return value for import.invoke() should be true", result);
82
//		assertEquals("Number of TaxonNames should be 5", 5, nameService.count());
83
	}
137
    public void testLongFile() {
138
        ImportResult result = defaultImport.invoke(configLong);
139
        String report = result.createReport().toString();
140
        System.out.println(report);
141

  
142
        Integer expected = 123;  //did not count yet
143
        Assert.assertEquals(expected, result.getNewRecords(Reference.class));
144

  
145
//        List<Reference> list = referenceService.list(Reference.class, null, null, null, null);
146
//        Assert.assertEquals("There should be 2 references, the article and the journal", 2, list.size());
147
//        for (Reference ref : list){
148
//            Assert.assertTrue(ref.getType() == ReferenceType.Article || ref.getType() == ReferenceType.Journal);
149
//            if (ref.getType() == ReferenceType.Article){
150
//                //title
151
//                Assert.assertEquals("Decorsella arborea, a second species in Decorsella (Violaceae), and Decorsella versus Rinorea",
152
//                        ref.getTitle());
153
//                //author
154
//                TeamOrPersonBase<?> author = ref.getAuthorship();
155
//                Assert.assertNotNull(author);
156
//                Assert.assertTrue(author.isInstanceOf(Person.class));
157
//                Person person = CdmBase.deproxy(author, Person.class);
158
//                //this may change in future depending on the correct formatting strategy
159
//                Assert.assertEquals("Carel C. H. Jongkind" ,person.getTitleCache());
160
//                Assert.assertEquals("Jongkind" ,person.getLastname());
161
//                Assert.assertEquals("Carel C. H." ,person.getFirstname());
162
//                //date
163
//                TimePeriod date = ref.getDatePublished();
164
//                Assert.assertEquals(Integer.valueOf(2017) ,date.getStartYear());
165
//                //vol
166
//                Assert.assertEquals("47(1)" ,ref.getVolume());
167
//                Assert.assertEquals("43-47" ,ref.getPages());
168
//
169
//                //doi
170
//                Assert.assertEquals(DOI.fromString("10.3372/wi.47.47105"),ref.getDoi());
171
//
172
//                //Abstract
173
//                Assert.assertEquals("Abstract: A new species of Violaceae, Decorsella arborea Jongkind, is described and illustrated. The new species differs from the only other species in the genus, D. paradoxa A. Chev., by the larger size of the plants, smaller leaves, more slender flowers, and stamen filaments that are free for a much larger part. Both species are from the Guineo-Congolian forest of tropical Africa. The differences between Decorsella and Rinorea are discussed. Confirming recent reports, some species of Rinorea can have zygomorphic flowers and some of these can be almost equal in shape to Decorsella flowers. Citation: Jongkind C. C. H. 2017: Decorsella arborea, a second species in Decorsella (Violaceae), and Decorsella versus Rinorea. ? Willdenowia 47: 43?47. doi: https://doi.org/10.3372/wi.47.47105 Version of record first published online on 13 February 2017 ahead of inclusion in April 2017 issue.",
174
//                        ref.getReferenceAbstract());
175
//
176
//                //TODO still missing Y1, Y2, M3, UR
177
//
178
//            }else if (ref.getType() == ReferenceType.Journal){
179
//                Assert.assertEquals("Willdenowia", ref.getTitle());
180
//                //or is this part of article?
181
//                Assert.assertEquals("Botanic Garden and Botanical Museum Berlin (BGBM)", ref.getPublisher());
182
//
183
//                //ISSN
184
//                Assert.assertEquals("0511-9618" ,ref.getIssn());
185
//
186
//            }else{
187
//                Assert.fail("Only an article and a journal should exist");
188
//            }
189
//        }
190

  
191
    }
84 192

  
85 193
    @Override
86 194
    public void createTestDataSet() throws FileNotFoundException {}
cdmlib-io/src/test/resources/eu/etaxonomy/cdm/io/reference/Acantholimon.ris
1
TY  - JOUR
2
AU  - Aguilar, J. F.
3
AU  - Feliner, G. N.
4
PY  - 2003
5
SP  - 430-447
6
ST  - Additive polymorphisms and reticulation in an ITS phylogeny of thrifts (Armeria, Plumbaginaceae)
7
T2  - Molecular Phylogenetics and Evolution
8
TI  - Additive polymorphisms and reticulation in an ITS phylogeny of thrifts (Armeria, Plumbaginaceae)
9
VL  - 28
10
ID  - 58
11
ER  - 
12

  
13

  
14
TY  - JOUR
15
AB  - A new species, Acantholimon karamanicum Akaychn & Dogan (Plumbaginaceae) is described and illustrated. The species grows on calcareous Mountain slopes in open Quercus sp. scrub, Astragalus sp. steppe, and inside Abies cilicica forest in Ermenek (Karaman). Diagnostic morphological characteristics from closely related species are discussed and conservation statuses of the species are given. A revised key to the Acantholimon species, with densely distichous spikes and outer bracts twice as long as the internodes, is also provided for the species found in Turkey.
16
AD  - Dogan, M
17
Hacettepe Univ, Dept Biol Educ, TR-06532 Ankara, Turkey
18
Hacettepe Univ, Dept Biol Educ, TR-06532 Ankara, Turkey
19
Hacettepe Univ, Dept Biol Educ, TR-06532 Ankara, Turkey
20
Middle E Tech Univ, Dept Sci Biol, TR-06531 Ankara, Turkey
21
AN  - WOS:000175945600008
22
AU  - Akaydin, G.
23
AU  - Doğan, M.
24
DO  - Doi 10.1092/54a2-0bep-Btyh-A3wc
25
IS  - 1
26
J2  - Isr J Plant Sci
27
LA  - English
28
N1  - 558bt
29
Times Cited:10
30
Cited References Count:12
31
PY  - 2002
32
SN  - 0792-9978
33
SP  - 67-71
34
ST  - A new species of Acantholimon Boiss. (Plumbaginaceae) from the western Taurus Mountains, Turkey
35
T2  - Israel Journal of Plant Sciences
36
TI  - A new species of Acantholimon Boiss. (Plumbaginaceae) from the western Taurus Mountains, Turkey
37
UR  - <Go to ISI>://WOS:000175945600008
38
VL  - 50
39
ID  - 17
40
ER  - 
41

  
42

  
43
TY  - JOUR
44
AU  - Aronne, G.
45
AU  - De Micco, V.
46
PY  - 2001
47
SP  - 789-794
48
ST  - Seasonal dimorphism in the Mediterranean Cistus incanus L. subsp. incanus
49
T2  - Annals of Botany
50
TI  - Seasonal dimorphism in the Mediterranean Cistus incanus L. subsp. incanus
51
VL  - 87
52
ID  - 44
53
ER  - 
54

  
55

  
56
TY  - CHAP
57
AU  - Assadi, M.
58
CY  - Tehran
59
PY  - 2005
60
SP  - XXX-XXX
61
ST  - Plumbaginaceae
62
T2  - Flora of Iran
63
TI  - Plumbaginaceae
64
VL  - 51
65
ID  - 53
66
ER  - 
67

  
68

  
69
TY  - JOUR
70
AU  - Assadi, M.
71
PY  - 2006
72
SP  - 114-120
73
ST  - Distribution patterns of the genus Acantholimon (Plumbaginaceae) in Iran
74
T2  - Iranian Journal of Botany
75
TI  - Distribution patterns of the genus Acantholimon (Plumbaginaceae) in Iran
76
VL  - 12
77
ID  - 51
78
ER  - 
79

  
80

  
81
TY  - JOUR
82
AB  - The new species Acantholimon doganii Y. Bagci, Dogu & Akaydin is described and illustrated. Diagnostic morphological characteristics in closely related species are discussed and the conservation status of the new species is analysed. A revised key to the Turkish Acantholimon species with persistent circinnate leaf basis is also provided.
83
AD  - Selcuk Univ, Dept Biol, Fac Sci & Art, TR-42031 Selcuklu, Konya, Turkey
84
Selcuk Univ, Dept Biol Educ, TR-42090 Meram, Konya, Turkey
85
Hacettepe Univ, Dept Biol Educ, TR-06800 Ankara, Turkey
86
AN  - WOS:000267130000013
87
AU  - Bagci, Y.
88
AU  - Dogu, S.
89
AU  - Akaydin, G.
90
DO  - 10.1111/j.1756-1051.2008.00390.x
91
IS  - 3
92
J2  - Nord J Bot
93
LA  - English
94
N1  - 459sq
95
Times Cited:1
96
Cited References Count:14
97
PY  - 2009
98
SN  - 0107-055x
99
SP  - 228-231
100
ST  - Acantholimon doganii sp nov (Plumbaginaceae) with persistent circinnate leaf bases, from Turkey
101
T2  - Nordic Journal of Botany
102
TI  - Acantholimon doganii sp nov (Plumbaginaceae) with persistent circinnate leaf bases, from Turkey
103
UR  - <Go to ISI>://WOS:000267130000013
104
VL  - 27
105
ID  - 52
106
ER  - 
107

  
108

  
109
TY  - BOOK
110
AU  - Boissier, E.
111
PB  - Lipsiae Herrmann
112
PY  - 1846
113
SE  - 69-81
114
ST  - Diagnoses Plantarum Orientalium Novarum
115
TI  - Diagnoses Plantarum Orientalium Novarum
116
VL  - 7
117
ID  - 21
118
ER  - 
119

  
120

  
121
TY  - CHAP
122
A2  - de Candolle, A. P.
123
AU  - Boissier, E. 
124
CY  - Paris
125
PB  - Treuttel et Wurz
126
PY  - 1848
127
ST  - Plumbaginales
128
T2  - Prodromus systematis naturalis regni vegetabilis.
129
TI  - Plumbaginales
130
ID  - 22
131
ER  - 
132

  
133

  
134
TY  - BOOK
135
AU  - Boissier, Pierre-Edmond
136
CY  - Geneva
137
KW  - Africa
138
Angiospermas
139
Asia
140
Europa
141
Flora
142
Península Ibérica
143
PY  - 1859
144
ST  - Diagnoses plantarum Orientalium novarum [...] Series secunda. N.º 4
145
TI  - Diagnoses plantarum Orientalium novarum [...] Series secunda. N.º 4
146
UR  - http://www.biodiversitylibrary.org/item/142679
147
ID  - 43
148
ER  - 
149

  
150

  
151
TY  - BOOK
152
AU  - Boissier, Pierre-Edmond
153
CY  - Basileae, Genevae
154
KW  - Africa
155
Angiospermas
156
Asia
157
Europa
158
Flora
159
PB  - Apud H. Georg, Bibliopolam
160
PY  - 1879
161
ST  - Flora Orientalis [...] Volumen quartum. Fasciculus secundus
162
TI  - Flora Orientalis [...] Volumen quartum. Fasciculus secundus
163
UR  - http://www.biodiversitylibrary.org/item/142697
164
ID  - 24
165
ER  - 
166

  
167

  
168
TY  - THES
169
AU  - Bokhari, M. H. 
170
CY  - Edinburgh
171
PB  - The University of Edinburgh
172
PY  - 1970
173
ST  - Taxonomic studies in S.W. Asian Plumbaginaceae
174
T2  - Faculty of Science
175
TI  - Taxonomic studies in S.W. Asian Plumbaginaceae
176
ID  - 2
177
ER  - 
178

  
179

  
180
TY  - CHAP
181
A2  - H., Davis P.
182
AU  - Bokhari, M. H.
183
AU  - Edmondson, J. R.
184
CY  - Edinburgh
185
PB  - Edinburgh University Press
186
PY  - 1982
187
SP  - 478-502
188
ST  - Acantholimon Boiss.
189
T2  - Flora of Turkey and the east Aegean islands
190
TI  - Acantholimon Boiss.
191
ID  - 25
192
ER  - 
193

  
194

  
195
TY  - JOUR
196
AU  - Bunge, A.
197
PY  - 1872
198
SP  - 1-72
199
ST  - Die Gattung Acantholimon Boiss.
200
T2  - Mémoires de l'Académie impériale des sciences de St. Pétersbourg
201
TI  - Die Gattung Acantholimon Boiss.
202
VL  - VII
203
ID  - 23
204
ER  - 
205

  
206

  
207
TY  - JOUR
208
AN  - WOS:A1990CU44200009
209
AU  - Christodoulakis, N. S.
210
AU  - Tsimbani, H.
211
AU  - Fasseas, C.
212
DA  - Mar
213
IS  - 3
214
J2  - Ann Bot-London
215
LA  - English
216
N1  - Cu442
217
Times Cited:25
218
Cited References Count:21
219
PY  - 1990
220
SN  - 0305-7364
221
SP  - 291-296
222
ST  - Leaf structural peculiarities in Sarcopoterium spinosum, a seasonally dimorphic subshrub
223
T2  - Annals of Botany
224
TI  - Leaf structural peculiarities in Sarcopoterium spinosum, a seasonally dimorphic subshrub
225
UR  - <Go to ISI>://WOS:A1990CU44200009
226
VL  - 65
227
ID  - 45
228
ER  - 
229

  
230

  
231
TY  - JOUR
232
AB  - Agamospermous species account for a large proportion of the species of Limonium. Agamospermy is indicated by uneven polyploidy or aneuploidy, low pollen stainability and by the presence of monomorphic self-incompatible populations. The taxonomic treatment of agamospermous taxa varies from recognition of all of them at the same specific rank or by utilising a range of ranks in the taxonomic hierarchy. The influence of evolutionary hypotheses on taxonomic systems is considered. Molecular data provide a means of measuring the genetical relationships of taxa and establishing groups in a taxonomic hierarchy.
233
AD  - Univ London Birkbeck Coll, Dept Biol, London WC1E 7HX, England
234
Univ Oxford, Dept Plant Sci, Oxford OX1 3RB, England
235
AN  - WOS:000077957200009
236
AU  - Cowan, R.
237
AU  - Ingrouille, M. J.
238
AU  - Lledó, M. D.
239
IS  - 3
240
J2  - Folia Geobot
241
KW  - apomixis
242
hybridisation
243
taxonomic hierarchy
244
species concepts
245
DNA
246
markers
247
plants
248
rbcl
249
rapd
250
aflp
251
LA  - English
252
N1  - 155qe
253
Times Cited:16
254
Cited References Count:50
255
PY  - 1998
256
SN  - 0015-5551
257
SP  - 353-366
258
ST  - The taxonomic treatment of agamosperms in the genus Limonium Mill. (Plumbaginaceae)
259
T2  - Folia Geobotanica
260
TI  - The taxonomic treatment of agamosperms in the genus Limonium Mill. (Plumbaginaceae)
261
UR  - <Go to ISI>://WOS:000077957200009
262
VL  - 33
263
ID  - 38
264
ER  - 
265

  
266

  
267
TY  - JOUR
268
AB  - Two new genera are described to include six taxa commonly considered members of Limoniastrum Heist. ex Fabr. Saharanthus M.B. Crespo & M.D. Lledo nom, nov. is proposed on the basis of the illegitimate Lerrouxia Caball., for a woody species from western Sahara, also known as Caballeroa Font Quer. Ceratolimon M.B. Crespo & M.D. Lledo gen, nov. is proposed for a group of five taxa (four species and one additional variety) of dwarf shrubs with horned spikelets, which show a disjunct distribution on the edges of the Sahara Desert. This latter name applies to most species formerly placed in the illegitimate Bubania. Types are indicated for all taxa, and identification keys are also presented. (C) 2000 The Linnean Society of London.
269
AD  - Univ Alicante, Dept Ciencias Ambientales & Recursos Nat Bot, E-03080 Alicante, Spain
270
Royal Bot Gardens, Herbarium, Richmond TW9 3AB, Surrey, England
271
AN  - WOS:000086098300005
272
AU  - Crespo, M. B.
273
AU  - Lledó, M. D.
274
DA  - Feb
275
IS  - 2
276
J2  - Bot J Linn Soc
277
KW  - biogeography
278
bubania
279
caballeroa
280
nomenclature
281
taxonomy
282
typification
283
LA  - English
284
N1  - 297tg
285
Times Cited:3
286
Cited References Count:29
287
PY  - 2000
288
SN  - 0024-4074
289
SP  - 165-174
290
ST  - Two new North African genera related to Limoniastrum (Plumbaginaceae)
291
T2  - Botanical Journal of the Linnean Society
292
TI  - Two new North African genera related to Limoniastrum (Plumbaginaceae)
293
UR  - <Go to ISI>://WOS:000086098300005
294
VL  - 132
295
ID  - 36
296
ER  - 
297

  
298

  
299
TY  - JOUR
300
AU  - Daniela, I.
301
IS  - 27
302
J2  - Newslett. Int. Organ. Pl. Biosyst.
303
PY  - 1997
304
SP  - 14
305
ST  - Chromosome data 11
306
T2  - Newslett. Int. Organ. Pl. Biosyst.
307
TI  - Chromosome data 11
308
VL  - 26
309
ID  - 54
310
ER  - 
311

  
312

  
313
TY  - JOUR
314
AB  - This study explores the response of the Irano-Turanian flora to Quaternary glacial–interglacial cycles in SW Asia. We use new fossil pollen data to assess variation in abundance of Cousinia Cass. (Compositae), a large genus typical for the Irano-Turanian flora, during these cycles. The results are compared with modern topography, tectonic and palaeoclimatic history, and recent phylogenetic data to explain the extremely high speciation rate and level of endemism as well as the modern geographical distribution of the genus. Cousinia is consistently well-represented in glacial-age and late-glacial pollen assemblages of NW Iran and E Anatolia. In the ~ 200,000-year pollen sequence from Lake Urmia, NW Iran, Cousinia pollen shows significant values and is nearly continuously represented during both the last glacial (~ 70 ka to Holocene) and penultimate glacial periods (~ 190 to 130 ka). In contrast, its pollen is less frequent and occurs only sporadically during the last interglacial period and the Holocene. This pattern suggests that Cousinia could not only withstand Quaternary glaciations, but was a significant part of the glacial-age landscapes of the Irano-Turanian territory. We argue that the extremely high rate of speciation and endemism of Cousinia is due to (i) the continuous presence of a complex topography in the Middle East and Central Asia since Tertiary times, which created a wide range of environmental niches and facilitated the formation and persistence of isolated populations over long periods, (ii) relatively stable climate during the late Miocene–Pliocene compared to the Quaternary period that caused small species range shifts and gene flow, and (iii) a dampened impact of multiple glacial–interglacial cycles on the mountain regions of SW Asia compared to the higher latitude European mountain ranges. This left an extensive, non-glaciated altitudinal zone for the survival of Irano-Turanian species, thereby reducing extinction during glacial periods. During interglacial periods, many Cousinia species may have been geographically isolated in high mountain “interglacial refugia” of the Irano-Turanian region. Overall, the combination of the above factors during the Neogene resulted in geographical isolation and reduced gene flow, thereby fostering allopatric speciation in Cousinia and probably also in many other speciose Irano-Turanian plant taxa.
315
AU  - Djamali, Morteza
316
AU  - Baumel, Alex
317
AU  - Brewer, Simon
318
AU  - Jackson, Stephen T.
319
AU  - Kadereit, Joachim W.
320
AU  - López-Vinyallonga, Sara
321
AU  - Mehregan, Iraj
322
AU  - Shabanian, Esmaeil
323
AU  - Simakova, Aleksandra
324
DA  - 2/15/
325
DO  - http://dx.doi.org/10.1016/j.revpalbo.2012.01.005
326
KW  - Quaternary glaciations
327
allopatric speciation
328
topographical heterogeneity
329
interglacial refugia
330
tectonic history
331
SW Asia
332
PY  - 2012
333
SN  - 0034-6667
334
SP  - 10-20
335
ST  - Ecological implications of Cousinia Cass. (Asteraceae) persistence through the last two glacial–interglacial cycles in the continental Middle East for the Irano-Turanian flora
336
T2  - Review of Palaeobotany and Palynology
337
TI  - Ecological implications of Cousinia Cass. (Asteraceae) persistence through the last two glacial–interglacial cycles in the continental Middle East for the Irano-Turanian flora
338
UR  - http://www.sciencedirect.com/science/article/pii/S0034666712000061
339
VL  - 172
340
ID  - 3
341
ER  - 
342

  
343

  
344
TY  - JOUR
345
AB  - This study investigates the role of climate in determining phytogeographic regions, focusing particularly on the lrano-Turanian floristic region in SW and Central Asia. A set of simple climatic variables and bioclimatic indices were used to prepare climate-space scatter plots and climate diagrams. The climate data were also subjected to multivariate analyses (PCA and Regression tree) in order to develop a bioclimatic characterization of the Irano-Turanian region in comparison with the adjacent Mediterranean, Saharo-Sindian, Euro-Siberian, and Central-Asiatic regions. Phytogeographic regions of SW and Central Asia display distinct bioclimatic spaces with small overlaps. The Irano-Turanian region is differentiated from surrounding regions by continentality, winter temperature, and precipitation seasonality. Continentality is the most important bioclimatic factor in differentiating it from the Mediterranean and Saharo-Sindian regions and is responsible for floristic differences among sub-regions of the Irano-Turanian region. In our case study, the Irano-Turanian region is a nearly independent bioclimatic unit, distinct from its surrounding regions. Hence, it is suggested that the term "Irano-Turanian bioclimate" be used to describe the climate of most of the continental Middle East and Central Asia. Among different sub-regions, the west-central part of this floristic region ("IT2 sub-region") is a major center of speciation and endemism. Our case study demonstrates that climate is a primary determinant of phytogeographic regionalization. Although modern climate and topography are strong control parameters on the floristic composition and geographical delimitation of the Irano-Turanian region, the complex paleogeographic and paleoclimatic history of SW Asia has also influenced the Tertiary and Quaternary evolution of the Irano-Turanian flora, with additional impacts by the long-lasting historic and present land-use in this region. Many lrano-Turanian montane species are threatened by global warming, and particular conservation measures are needed to protect the Irano-Turanian flora in all sub-regions. (C) 2012 Elsevier GmbH. All rights reserved.
346
AD  - CNRS, UMR 7263, IMBE, IRD 237, F-13545 Aix En Provence 04, France
347
Univ Wyoming, Dept Bot 3165, Laramie, WY 82071 USA
348
Univ Bielefeld, Dept Ecol, D-33619 Bielefeld, Germany
349
Univ Wyoming, Berry Biodivers Conservat Ctr, Program Ecol 3622, Laramie, WY 82071 USA
350
AN  - WOS:000304898500001
351
AU  - Djamali, M.
352
AU  - Brewer, S.
353
AU  - Breckle, S. W.
354
AU  - Jackson, S. T.
355
DO  - 10.1016/j.flora.2012.01.009
356
IS  - 4
357
J2  - Flora
358
KW  - phytogeography
359
climate
360
continentality
361
flora
362
middle east
363
central asia
364
subtropical anticyclones
365
photosynthetic pathways
366
winter temperatures
367
late-quaternary
368
vegetation
369
dynamics
370
holocene
371
flora
372
east
373
precipitation
374
LA  - English
375
N1  - 953uk
376
Times Cited:8
377
Cited References Count:89
378
PY  - 2012
379
SN  - 0367-2530
380
SP  - 237-249
381
ST  - Climatic determinism in phytogeographic regionalization: A test from the Irano-Turanian region, SW and Central Asia
382
T2  - Flora
383
TI  - Climatic determinism in phytogeographic regionalization: A test from the Irano-Turanian region, SW and Central Asia
384
UR  - <Go to ISI>://WOS:000304898500001
385
VL  - 207
386
ID  - 50
387
ER  - 
388

  
389

  
390
TY  - JOUR
391
AB  - We describe the spatial distribution of the genus Acantholimon Boiss. (Plumbaginaceae) taxa in Turkey, and assess the role that environmental variables may be playing on this distribution. We collected a large number of specimens from 418 geo-referenced sampling sites between 2000 and 2004, and established a point database using geographic information systems (GIS) software. As a result, we identified and mapped 67 taxa; 43 of the determined taxa appear to be endemic. We re-evaluated the current conservation status of the taxa at a national level using recent IUCN Red List categories. In addition, we extracted the corresponding environmental variables of each determined point from the updated and available environmental raster map layers of Turkey and analysed the obtained taxa and environmental data by Hierarchical Clustering and Canonical Correspondence Analysis (CCA). Hierarchical Clustering delineated the subgroups, which have similarities at various levels in respect to environmental variables. The CCA results indicated that 8 environmental variables including longitude, distance to sea, maximum temperature, mean temperature, minimum temperature, potential evapotranspiration, elevation, and precipitation are the most effective in explaining the spatial distribution of the 18 Acantholimon taxa in Turkey.
392
AD  - Gaziosmanpasa Univ, Geog Informat Syst & Remote Sensing Unit, Dept Soil Sci, TR-60240 Tasliciftlik, Tokat, Turkey
393
Middle E Tech Univ, Dept Biol Sci, TR-06531 Ankara, Turkey
394
Hacettepe Univ, Dept Biol Educ, TR-06800 Ankara, Turkey
395
AN  - WOS:000289044300001
396
AU  - Doğan, H. M.
397
AU  - Doğan, M.
398
AU  - Akaydin, G.
399
AU  - Celep, F.
400
DO  - 10.3906/bot-0910-193
401
IS  - 2
402
J2  - Turk J Bot
403
KW  - acantholimon
404
anatolia
405
biodiversity
406
ecology
407
gis
408
mapping
409
spatial analysis
410
iucn
411
canonical correspondence-analysis
412
sect. staticopsis plumbaginaceae
413
boiss. plumbaginaceae
414
ecology
415
ankara
416
israel
417
LA  - English
418
N1  - 743un
419
Times Cited:3
420
Cited References Count:34
421
PY  - 2011
422
SN  - 1300-008x
423
SP  - 91-110
424
ST  - Mapping and analysing the diversity of the genus Acantholimon taxa in Turkey by geographic information systems (GIS)
425
T2  - Turkish Journal of Botany
426
TI  - Mapping and analysing the diversity of the genus Acantholimon taxa in Turkey by geographic information systems (GIS)
427
UR  - <Go to ISI>://WOS:000289044300001
428
VL  - 35
429
ID  - 8
430
ER  - 
431

  
432

  
433
TY  - JOUR
434
AB  - A new species, Acantholimon avanosicum Dogan & Akaydin in the Plumbaginaceae is described and illustrated. The species grows on argillaceaus slopes in Central Anatolia, Turkey. The diagnostic morphological characters that distinguish it from closely related taxa are discussed. A revised key to Acantholimon species in Turkey with densely distichous simple spikes is presented. (C) 2002 The Linnean Society of London, Botanical Journal of the Linnean Society, 2002, 138, 365-368.
435
AD  - Dogan, M
436
Middle E Tech Univ, Dept Biol Sci, TR-06531 Ankara, Turkey
437
Middle E Tech Univ, Dept Biol Sci, TR-06531 Ankara, Turkey
438
Middle E Tech Univ, Dept Biol Sci, TR-06531 Ankara, Turkey
439
Hacettepe Univ, Dept Biol Educ, TR-06532 Ankara, Turkey
440
AN  - WOS:000174806000008
441
AU  - Dogan, M.
442
AU  - Akaydin, G.
443
DA  - Mar
444
DO  - DOI 10.1046/j.1095-8339.2002.00027.x
445
IS  - 3
446
J2  - Bot J Linn Soc
447
KW  - conservation
448
distribution
449
LA  - English
450
N1  - 538gc
451
Times Cited:11
452
Cited References Count:10
453
PY  - 2002
454
SN  - 0024-4074
455
SP  - 365-368
456
ST  - A new species of Acantholimon Boiss. (Plumbaginaceae) from Turkey
457
T2  - Botanical Journal of the Linnean Society
458
TI  - A new species of Acantholimon Boiss. (Plumbaginaceae) from Turkey
459
UR  - <Go to ISI>://WOS:000174806000008
460
VL  - 138
461
ID  - 16
462
ER  - 
463

  
464

  
465
TY  - JOUR
466
AB  - A new species, Acantholimon birandii, is described and illustrated from Karaman, Turkey.
467
AD  - Middle E Tech Univ, Dept Biol Sci, TR-06531 Ankara, Turkey
468
Hacettepe Univ, Dept Biol Educ, TR-06532 Ankara, Turkey
469
AN  - WOS:000176737100005
470
AU  - Doğan, M.
471
AU  - Akaydin, G.
472
DO  - DOI 10.1111/j.1756-1051.2001.tb00800.x
473
IS  - 5
474
J2  - Nord J Bot
475
LA  - English
476
N1  - 571ug
477
Times Cited:9
478
Cited References Count:13
479
PY  - 2001
480
SN  - 0107-055x
481
SP  - 481-484
482
ST  - A new species, Acantholimon birandii (Plumbaginaceae), from the Central Anatolian Steppe in Turkey
483
T2  - Nordic Journal of Botany
484
TI  - A new species, Acantholimon birandii (Plumbaginaceae), from the Central Anatolian Steppe in Turkey
485
UR  - <Go to ISI>://WOS:000176737100005
486
VL  - 21
487
ID  - 18
488
ER  - 
489

  
490

  
491
TY  - JOUR
492
AB  - Acantholimon anatolicum Dogan & Akaydin sp. nov. (Plumbaginaceae) is described and illustrated. The species grows on deep gypsum-rich sandy soil on eroded mountain slopes between Cayirhan and Nallihan in Ankara. Diagnostic morphological characters that discern it from closely related species are discussed and its conservation status is indicated. A revised key to Acantholimon species with dense terminal spikes and excurrent scapes is given for the species found in Turkey. (C) The Linnean Society of London.
493
AD  - Dogan, M
494
Middle E Tech Univ, Dept Biol Sci, TR-06531 Ankara, Turkey
495
Middle E Tech Univ, Dept Biol Sci, TR-06531 Ankara, Turkey
496
Middle E Tech Univ, Dept Biol Sci, TR-06531 Ankara, Turkey
497
Hacettepe Univ, Dept Biol Educ, TR-06532 Ankara, Turkey
498
AN  - WOS:000180083100008
499
AU  - Doğan, M.
500
AU  - Akaydin, G.
501
DA  - Dec
502
DO  - DOI 10.1046/j.1095-8339.2002.00095.x
503
IS  - 4
504
J2  - Bot J Linn Soc
505
KW  - central anatolia
506
conservation
507
distribution
508
LA  - English
509
N1  - 629zq
510
Times Cited:9
511
Cited References Count:20
512
PY  - 2002
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff

Add picture from clipboard (Maximum size: 40 MB)