##### Coverage Report - com.ctrip.apollo.portal.service.txtresolver.PropertyResolver

Classes in this File | Line Coverage | Branch Coverage | Complexity
PropertyResolver |
95% | 84/88
87% | 63/72
| 3.466666666666667;3.467

1 |  |
     package com.ctrip.apollo.portal.service.txtresolver;
2 |  |
3 |  |
     import com.ctrip.apollo.core.dto.ItemChangeSets;
4 |  |
     import com.ctrip.apollo.core.dto.ItemDTO;
5 |  |
     import com.ctrip.apollo.core.exception.BadRequestException;
6 |  |
     import com.ctrip.apollo.core.utils.StringUtils;
7 |  |
     import com.ctrip.apollo.common.utils.BeanUtils;
8 |  |
9 |  |
     import org.springframework.stereotype.Component;
10 |  |
11 |  |
     import java.util.HashMap;
12 |  |
     import java.util.HashSet;
13 |  |
     import java.util.List;
14 |  |
     import java.util.Map;
15 |  |
     import java.util.Set;
16 |  |
17 |  |
     /**
18 |  |
      * normal property file resolver.
19 |  |
      * update comment and blank item implement by create new item and delete old item.
20 |  |
      * update normal key/value item implement by update.
21 |  |
      */
22 |  |
     @Component
23 | 1 |
     public class PropertyResolver implements ConfigTextResolver {
24 |  |
25 |  |
       private static final String KV_SEPARATOR = "=";
26 |  |
       private static final String ITEM_SEPARATOR = "\n";
27 |  |
28 |  |
       @Override
29 |  |
       public ItemChangeSets resolve(long namespaceId, String configText, List<ItemDTO> baseItems) {
30 |  |
31 | 11 |
         if (StringUtils.isEmpty(configText)){
32 | 1 |
           throw new BadRequestException("config text can not be empty");
33 |  |
         }
34 |  |
35 | 10 |
         Map<Integer, ItemDTO> oldLineNumMapItem = BeanUtils.mapByKey("lineNum", baseItems);
36 | 10 |
         Map<String, ItemDTO> oldKeyMapItem = BeanUtils.mapByKey("key", baseItems);
37 |  |
38 |  |
         //remove comment and blank item map.
39 | 10 |
         oldKeyMapItem.remove("");
40 |  |
41 | 10 |
         String[] newItems = configText.split(ITEM_SEPARATOR);
42 |  |
43 | 10 |
         if (isHasRepeatKey(newItems)){
44 | 0 |
           throw new BadRequestException("config text has repeat key please check.");
45 |  |
         }
46 |  |
47 | 10 |
         ItemChangeSets changeSets = new ItemChangeSets();
48 | 10 |
         Map<Integer, String> newLineNumMapItem = new HashMap<Integer, String>();//use for delete blank and comment item
49 | 10 |
         int lineCounter = 1;
50 | 43 |
         for (String newItem : newItems) {
51 | 33 |
           newItem = newItem.trim();
52 | 33 |
           newLineNumMapItem.put(lineCounter, newItem);
53 | 33 |
           ItemDTO oldItemByLine = oldLineNumMapItem.get(lineCounter);
54 |  |
55 |  |
           //comment item
56 | 33 |
           if (isCommentItem(newItem)) {
57 |  |
58 | 5 |
             handleCommentLine(namespaceId, oldItemByLine, newItem, lineCounter, changeSets);
59 |  |
60 |  |
             //blank item
61 | 28 |
           } else if (isBlankItem(newItem)) {
62 |  |
63 | 4 |
             handleBlankLine(namespaceId, oldItemByLine, lineCounter, changeSets);
64 |  |
65 |  |
             //normal item
66 |  |
           } else {
67 | 24 |
             handleNormalLine(namespaceId, oldKeyMapItem, newItem, lineCounter, changeSets);
68 |  |
           }
69 |  |
70 | 33 |
           lineCounter++;
71 |  |
         }
72 |  |
73 | 10 |
         deleteCommentAndBlankItem(oldLineNumMapItem, newLineNumMapItem, changeSets);
74 | 10 |
         deleteNormalKVItem(oldKeyMapItem, changeSets);
75 |  |
76 | 10 |
         return changeSets;
77 |  |
       }
78 |  |
79 |  |
       private boolean isHasRepeatKey(String[] newItems){
80 | 10 |
         Set<String> keys = new HashSet<>();
81 | 10 |
         int lineCounter = 1;
82 | 10 |
         int keyCount = 0;
83 | 43 |
         for (String item: newItems){
84 | 33 |
           if (!isCommentItem(item) && !isBlankItem(item)){
85 | 24 |
             keyCount++;
86 | 24 |
             String[] kv = parseKeyValueFromItem(item);
87 | 24 |
             if (kv != null) {
88 | 24 |
               keys.add(kv[0]);
89 |  |
             }else {
90 | 0 |
               throw new BadRequestException("line:" + lineCounter + " key value must separate by '='");
91 |  |
             }
92 |  |
           }
93 | 33 |
           lineCounter ++;
94 |  |
         }
95 |  |
96 | 10 |
         return keyCount > keys.size();
97 |  |
       }
98 |  |
99 |  |
       private String[] parseKeyValueFromItem(String item){
100 | 48 |
         int kvSeparator = item.indexOf(KV_SEPARATOR);
101 | 48 |
         if (kvSeparator == -1) {
102 | 0 |
           return null;
103 |  |
         }
104 |  |
105 | 48 |
         String[] kv = new String[2];
106 | 48 |
         kv[0] = item.substring(0, kvSeparator).trim();
107 | 48 |
         kv[1] = item.substring(kvSeparator + 1, item.length()).trim();
108 | 48 |
         return kv;
109 |  |
       }
110 |  |
111 |  |
       private void handleCommentLine(Long namespaceId, ItemDTO oldItemByLine, String newItem, int lineCounter, ItemChangeSets changeSets) {
112 | 5 |
         String oldComment = oldItemByLine == null ? "" : oldItemByLine.getComment();
113 |  |
         //create comment. implement update comment by delete old comment and create new comment
114 | 5 |
         if (!(isCommentItem(oldItemByLine) && newItem.equals(oldComment))) {
115 | 4 |
           changeSets.addCreateItem(buildCommentItem(0l, namespaceId, newItem, lineCounter));
116 |  |
         }
117 | 5 |
       }
118 |  |
119 |  |
       private void handleBlankLine(Long namespaceId, ItemDTO oldItem, int lineCounter, ItemChangeSets changeSets) {
120 | 4 |
         if (!isBlankItem(oldItem)) {
121 | 3 |
           changeSets.addCreateItem(buildBlankItem(0l, namespaceId, lineCounter));
122 |  |
         }
123 | 4 |
       }
124 |  |
125 |  |
       private void handleNormalLine(Long namespaceId, Map<String, ItemDTO> keyMapOldItem, String newItem,
126 |  |
                                        int lineCounter, ItemChangeSets changeSets) {
127 |  |
128 | 24 |
         String[] kv = parseKeyValueFromItem(newItem);
129 |  |
130 | 24 |
         if (kv == null) {
131 | 0 |
           throw new BadRequestException("line:" + lineCounter + " key value must separate by '='");
132 |  |
         }
133 |  |
134 | 24 |
         String newKey = kv[0];
135 | 24 |
         String newValue = kv[1];
136 |  |
137 | 24 |
         ItemDTO oldItem = keyMapOldItem.get(newKey);
138 |  |
139 | 24 |
         if (oldItem == null) {//new item
140 | 5 |
           changeSets.addCreateItem(buildNormalItem(0l, namespaceId, newKey, newValue, "", lineCounter));
141 | 19 |
         } else if (!newValue.equals(oldItem.getValue()) || lineCounter != oldItem.getLineNum()){//update item
142 | 30 |
           changeSets.addUpdateItem(
143 | 15 |
               buildNormalItem(oldItem.getId(), namespaceId, newKey, newValue, oldItem.getComment(),
144 |  |
                               lineCounter));
145 |  |
         }
146 | 24 |
         keyMapOldItem.remove(newKey);
147 | 24 |
       }
148 |  |
149 |  |
       private boolean isCommentItem(ItemDTO item) {
150 | 33 |
         return item != null && "".equals(item.getKey())
151 | 8 |
                && (item.getComment().startsWith("#") || item.getComment().startsWith("!"));
152 |  |
       }
153 |  |
154 |  |
       private boolean isCommentItem(String line) {
155 | 66 |
         return line != null && (line.startsWith("#") || line.startsWith("!"));
156 |  |
       }
157 |  |
158 |  |
       private boolean isBlankItem(ItemDTO item) {
159 | 35 |
         return item != null && "".equals(item.getKey()) && "".equals(item.getComment());
160 |  |
       }
161 |  |
162 |  |
       private boolean isBlankItem(String line) {
163 | 60 |
         return "".equals(line);
164 |  |
       }
165 |  |
166 |  |
       private void deleteNormalKVItem(Map<String, ItemDTO> baseKeyMapItem, ItemChangeSets changeSets) {
167 |  |
         //surplus item is to be deleted
168 | 10 |
         for (Map.Entry<String, ItemDTO> entry : baseKeyMapItem.entrySet()) {
169 | 4 |
           changeSets.addDeleteItem(entry.getValue());
170 | 4 |
         }
171 | 10 |
       }
172 |  |
173 |  |
       private void deleteCommentAndBlankItem(Map<Integer, ItemDTO> oldLineNumMapItem,
174 |  |
                                              Map<Integer, String> newLineNumMapItem,
175 |  |
                                              ItemChangeSets changeSets) {
176 |  |
177 | 10 |
         for (Map.Entry<Integer, ItemDTO> entry : oldLineNumMapItem.entrySet()) {
178 | 31 |
           int lineNum = entry.getKey();
179 | 31 |
           ItemDTO oldItem = entry.getValue();
180 | 31 |
           String newItem = newLineNumMapItem.get(lineNum);
181 |  |
182 |  |
           //1. old is blank by now is not
183 |  |
           //2.old is comment by now is not exist or modified
184 | 31 |
           if ((isBlankItem(oldItem) && !isBlankItem(newItem))
185 | 28 |
               || isCommentItem(oldItem) && (newItem == null || !newItem.equals(oldItem.getComment()))) {
186 | 6 |
             changeSets.addDeleteItem(oldItem);
187 |  |
           }
188 | 31 |
         }
189 | 10 |
       }
190 |  |
191 |  |
       private ItemDTO buildCommentItem(Long id, Long namespaceId, String comment, int lineNum) {
192 | 4 |
         return buildNormalItem(id, namespaceId, "", "", comment, lineNum);
193 |  |
       }
194 |  |
195 |  |
       private ItemDTO buildBlankItem(Long id, Long namespaceId, int lineNum) {
196 | 3 |
         return buildNormalItem(id, namespaceId, "", "", "", lineNum);
197 |  |
       }
198 |  |
199 |  |
       private ItemDTO buildNormalItem(Long id, Long namespaceId, String key, String value, String comment, int lineNum) {
200 | 27 |
         ItemDTO item = new ItemDTO(key, value, comment, lineNum);
201 | 27 |
         item.setId(id);
202 | 27 |
         item.setNamespaceId(namespaceId);
203 | 27 |
         return item;
204 |  |
       }
205 |  |
     }